Elixir中的HashDict和OTP GenServer上下文

时间:2015-12-14 15:47:15

标签: dictionary elixir otp gen-server

我在OTP中使用HashDict功能时遇到问题。我想将一个GenServer流程用于put,将另一个流程用于fetch。当我尝试实现这个时,我可以在从同一个GenServer调用时从HashDict放置和获取项目;它完美地工作(在下面的例子中MyServerA)。但是当我使用一个GenServerput而另一个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时,它再也没有返回。所以我想知道我是否错误地处理了这个状态?任何帮助,感激不尽?

由于

2 个答案:

答案 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

希望这有帮助。