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

希望对您有所帮助。