【问题标题】:Working with Elixir ETS across different processes在不同流程中使用 Elixir ETS
【发布时间】:2016-03-21 05:06:54
【问题描述】:

我尝试围绕 ETS 实现一个非常简单的示例,但没有取得多大成功。我想让多个 worker 进程写入 ETS 表,然后让一个(和不同的)读取器进程定期检索值作为总和。我似乎无法在没有崩溃的情况下插入表格,并且阅读器在执行时返回零......这是我的代码,非常感谢您的帮助:

Supervisor 模块:

defmodule Stackex do
  use Application

  @noOfWriters 1

  def start(_type, _args) do
    import Supervisor.Spec, warn: false

    Stackex.Reader.start_link
    Stackex.Table.start_link

    opts = [strategy: :one_for_one, name: Stackex.Supervisor]

    children =
      for i <- 1..@noOfWriters do
        worker(Stackex.Writer, [i], id: i)
      end

    Supervisor.start_link(children, opts)

  end
end

Writer 模块:

defmodule Stackex.Writer do
  use GenServer

  def start_link(id) do
    GenServer.start_link(__MODULE__,{id})
  end

  def init({id}) do
    state = %{writer_id: id, value: value}
    schedule_work()
    {:ok, state}
  end

  def handle_info(:update, state) do
    Stackex.Table.add_kvp(state)
    update = %{writer_id: state.writer_id, value: value}
    schedule_work()
    {:noreply, update}
  end

  defp value do
    :random.seed(:erlang.now())
    :random.uniform(10) 
  end

  defp schedule_work() do
    Process.send_after(self, :update, 1000)
  end
end

Table 模块:

defmodule Stackex.Table do
  use GenServer

  def start_link do
    GenServer.start_link(__MODULE__,[], name: __MODULE__)
  end

  def init do
    {:ok, :ets.new(:table, [:set, :named_table])}
  end

  #Client API
  def add_kvp(update) do
    GenServer.cast __MODULE__, {:load, update}
  end

  def get_kvp_sum do
    GenServer.call __MODULE__, {:sum}
  end

  ###### Server Callback Functions ########

  def handle_cast({:load, update}, state) do
    %{writer_id: key, value: value} = update
    {:noreply, :ets.insert(:table, {key, value})}
  end

  #Return total system load
  def handle_call({:sum}, _from, state) do
    sum = :ets.foldl(fn({{_,v}, acc}) -> v + acc end, 0, :table)
    {:reply, sum, state}
  end
end

Reader 模块:

defmodule Stackex.Reader do
  use GenServer

  def start_link do
    GenServer.start_link(__MODULE__, [], name: __MODULE__)
  end

  def init do
    schedule_update
    {:ok, nil}
  end

  def handle_info(:update) do
    sum = Stackex.Table.get_kvp_sum
    IO.puts("Sum #{sum}")

    schedule_update
    {:noreply, nil}
  end

  defp schedule_update do
    Process.send_after(self, :update, 2000)
  end
end

【问题讨论】:

    标签: elixir ets


    【解决方案1】:

    在回答标题中的问题时,您可以通过在调用 :ets.new/2 时在选项中传递 :public 来使其他进程可以访问命名的 ets 表

    但是根据您发布的代码,您没有多个进程访问您的表。只有一个Stackex.Table genserver 启动。看起来您甚至不需要将其设为 :named_table,因为您只需将 pid 传递到您的 state 变量中,只需对代码进行很少的更改。

    看起来您的直接问题是您将错误的变量传递给add_kvp 方法(state 而不是update)。

    【讨论】:

      猜你喜欢
      • 2021-12-21
      • 2017-10-17
      • 2019-06-11
      • 2019-02-05
      • 2019-10-23
      • 1970-01-01
      • 2016-01-17
      • 2021-04-20
      • 1970-01-01
      相关资源
      最近更新 更多