Elixir 中的 HashDict 和 OTP GenServer 上下文
HashDict and OTP GenServer context within Elixir
我在使用 OTP 中的 HashDict
函数时遇到问题。我想对 put
使用一个 GenServer
进程,对 fetch
使用另一个进程。当我尝试实现它时,我可以在从同一个 GenServer 调用时从 HashDict
中放置和获取项目;它工作得很好(下面示例中的 MyServerA
)。但是,当我将一个 GenServer
用于 put
并将另一个用于 fetch
时,获取实现不起作用。为什么是这样?大概是因为我需要在三个不同的进程之间传递 HashDict
数据结构?
下面的代码示例:
我使用一个简单的调用将一些状态发送到 MyServerB
:
MyServerA.add_update(state)
对于 MyServerB
我已经实现了 HashDict
如下:
defmodule MyServerB do
use GenServer
def start_link do
GenServer.start_link(__MODULE__,[], name: __MODULE__)
end
def init([]) do
#Initialise HashDict to store state
d = HashDict.new
{:ok, d}
end
#Client API
def add_update(update) do
GenServer.cast __MODULE__, {:add, update}
end
def get_state(window) do
GenServer.call __MODULE__, {:get, key}
end
# Server APIs
def handle_cast({:add, update}, dict) do
%{key: key} = update
dict = HashDict.put(dict, key, some_Value)
{:noreply, dict}
end
def handle_call({:get, some_key}, _from, dict) do
value = HashDict.fetch!(dict, some_key)
{:reply, value, dict}
end
end
因此,如果我从另一个进程使用 MyServerB.get_state(dict,some_key)
,我似乎无法 return HashDict
...
的内容
更新:
所以如果我使用 ETS,我会遇到这样的情况:
def init do
ets = :ets.new(:my_table,[:ordered_set, :named_table])
{:ok, ets}
end
def handle_cast({:add, update}, state) do
update = :ets.insert(:my_table, {key, value})
{:noreply, ups}
end
def handle_call({:get, some_key}, _from, state) do
sum = :ets.foldl(fn({{key},{value}}, acc)
when key == some_Key -> value + acc
(_, acc) ->
acc
end, 0, :my_table)
{:reply, sum, state}
end
所以,cast
再次起作用 - 当我检查 observer
时,我可以看到它填满了我的键值对。但是,当我尝试 call
时,它又 return 什么都没有了。所以我想知道我是否错误地处理了状态?任何帮助,感激不尽??
谢谢
您的问题在于此语句:
I would like to use one GenServer process to put and a different one to fetch.
在 Elixir 中进程不能共享状态。所以你不能让一个进程处理数据,而另一个进程直接读取它。例如,您可以将 HashDict 存储在一个进程中,然后让另一个进程向第一个请求数据的进程发送消息。这会让它看起来像你描述的那样,但在幕后它仍然会让所有交易都经过第一个过程。有以 distributed/concurrent 方式执行此操作的技术,以便利用多个内核,但这可能比您目前希望做的工作更多。
看看 ETS,它将允许您创建 public table 并从多个进程访问数据。
ETS 是必经之路。在 GenServer 之间共享一个 HashDict 作为状态是不可能的。
我真的不知道你是如何测试你的代码的,但是默认情况下 ETS 已将读写并发设置为 false。例如,如果您对并发读写没有问题,那么您可以将 init 函数更改为:
def init do
ets = :ets.new :my_table, [:ordered_set, :named_table,
read_concurrency: true,
write_concurrency: true]
{:ok, ets}
end
希望对您有所帮助。
我在使用 OTP 中的 HashDict
函数时遇到问题。我想对 put
使用一个 GenServer
进程,对 fetch
使用另一个进程。当我尝试实现它时,我可以在从同一个 GenServer 调用时从 HashDict
中放置和获取项目;它工作得很好(下面示例中的 MyServerA
)。但是,当我将一个 GenServer
用于 put
并将另一个用于 fetch
时,获取实现不起作用。为什么是这样?大概是因为我需要在三个不同的进程之间传递 HashDict
数据结构?
下面的代码示例:
我使用一个简单的调用将一些状态发送到 MyServerB
:
MyServerA.add_update(state)
对于 MyServerB
我已经实现了 HashDict
如下:
defmodule MyServerB do
use GenServer
def start_link do
GenServer.start_link(__MODULE__,[], name: __MODULE__)
end
def init([]) do
#Initialise HashDict to store state
d = HashDict.new
{:ok, d}
end
#Client API
def add_update(update) do
GenServer.cast __MODULE__, {:add, update}
end
def get_state(window) do
GenServer.call __MODULE__, {:get, key}
end
# Server APIs
def handle_cast({:add, update}, dict) do
%{key: key} = update
dict = HashDict.put(dict, key, some_Value)
{:noreply, dict}
end
def handle_call({:get, some_key}, _from, dict) do
value = HashDict.fetch!(dict, some_key)
{:reply, value, dict}
end
end
因此,如果我从另一个进程使用 MyServerB.get_state(dict,some_key)
,我似乎无法 return HashDict
...
更新:
所以如果我使用 ETS,我会遇到这样的情况:
def init do
ets = :ets.new(:my_table,[:ordered_set, :named_table])
{:ok, ets}
end
def handle_cast({:add, update}, state) do
update = :ets.insert(:my_table, {key, value})
{:noreply, ups}
end
def handle_call({:get, some_key}, _from, state) do
sum = :ets.foldl(fn({{key},{value}}, acc)
when key == some_Key -> value + acc
(_, acc) ->
acc
end, 0, :my_table)
{:reply, sum, state}
end
所以,cast
再次起作用 - 当我检查 observer
时,我可以看到它填满了我的键值对。但是,当我尝试 call
时,它又 return 什么都没有了。所以我想知道我是否错误地处理了状态?任何帮助,感激不尽??
谢谢
您的问题在于此语句:
I would like to use one GenServer process to put and a different one to fetch.
在 Elixir 中进程不能共享状态。所以你不能让一个进程处理数据,而另一个进程直接读取它。例如,您可以将 HashDict 存储在一个进程中,然后让另一个进程向第一个请求数据的进程发送消息。这会让它看起来像你描述的那样,但在幕后它仍然会让所有交易都经过第一个过程。有以 distributed/concurrent 方式执行此操作的技术,以便利用多个内核,但这可能比您目前希望做的工作更多。
看看 ETS,它将允许您创建 public table 并从多个进程访问数据。
ETS 是必经之路。在 GenServer 之间共享一个 HashDict 作为状态是不可能的。
我真的不知道你是如何测试你的代码的,但是默认情况下 ETS 已将读写并发设置为 false。例如,如果您对并发读写没有问题,那么您可以将 init 函数更改为:
def init do
ets = :ets.new :my_table, [:ordered_set, :named_table,
read_concurrency: true,
write_concurrency: true]
{:ok, ets}
end
希望对您有所帮助。