我在OTP中使用HashDict
功能时遇到问题。我想将一个GenServer
流程用于put
,将另一个流程用于fetch
。当我尝试实现这个时,我可以在从同一个GenServer调用时从HashDict
放置和获取项目;它完美地工作(在下面的例子中MyServerA
)。但是当我使用一个GenServer
到put
而另一个fetch
时,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)
的其他流程,我似乎无法返回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
时,它再也没有返回。所以我想知道我是否错误地处理了这个状态?任何帮助,感激不尽?
由于
答案 0 :(得分:4)
你的问题在于这句话:
我想使用一个GenServer进程和另一个进行提取。
在Elixir进程中无法共享状态。因此,您不能拥有一个包含数据的流程,而另一个流程可以直接读取数据。例如,您可以将HashDict存储在一个进程中,然后让另一个进程向第一个请求数据发送消息。这将使它看起来像你描述的那样,但是在幕后它仍然会让所有事务都经历第一个过程。有一些技术可以以分布式/并发方式执行此操作,以便利用多个核心,但这可能比您目前要做的工作更多。
查看ETS,它将允许您创建公共表并访问多个进程中的数据。
答案 1 :(得分:1)
ETS是要走的路。共享HashDict作为GenServers之间的状态是不可能的。
我真的不知道你是如何测试你的代码的,但是默认情况下ETS的读写并发性为false。例如,如果您在同时读取或写入时没有问题,则可以将init函数更改为:
def init do
ets = :ets.new :my_table, [:ordered_set, :named_table,
read_concurrency: true,
write_concurrency: true]
{:ok, ets}
end
希望这有帮助。