有没有可能在Elixir/Erlang中向所有子进程发送消息?

7

假设我在Elixir中生成多个子进程。

defmodule Child do
  def start(name) do
    receive do
      msg -> IO.puts "Message received by #{name}: #{inspect msg}"
    end
  end
end

defmodule Parent do
  def main do
    child1 = spawn_link (fn -> Child.start("1") end)
    child2 = spawn_link (fn -> Child.start("2") end)
    child3 = spawn_link (fn -> Child.start("3") end)
  end
end

我是否有任何方法可以向当前进程(或其他进程)的所有子进程发送消息?

send_to_children self(), "hello to all children"

我是指,有没有一种方式可以告诉运行时向当前进程链接的所有进程广播消息?当然,我可以将所有生成的pid存储在某种数据结构中并循环遍历以执行此操作,但如果有一种规范的方法来执行此操作,似乎效率更高且错误更少。

2个回答

8

由于您使用了spawn_link,您可以获取所有链接进程的列表并向它们发送消息:

defmodule Child do
  def start(name) do
    receive do
      msg -> IO.puts "Message received by #{name}: #{inspect msg}"
    end
  end
end

defmodule Parent do
  def main do
    child1 = spawn_link (fn -> Child.start("1") end)
    child2 = spawn_link (fn -> Child.start("2") end)
    child3 = spawn_link (fn -> Child.start("3") end)
    {:links, links} = Process.info(self, :links)
    for pid <- links do
      send pid, :foo
    end
  end
end

Parent.main
:timer.sleep(1000)

输出:

Message received by 2: :foo
Message received by 1: :foo
Message received by 3: :foo

我认为直接获取一个进程的子进程列表是不可能的:http://erlang.org/pipermail/erlang-questions/2013-April/073125.html。如果您从Supervisor中生成它们,则有方法,但对于任意情况则没有。


5
这种方法的问题在于链接是双向的。因此,如果你在监督器下启动子进程,那么监督器(即父进程)也会在列表中,并接收消息。最好的方法是存储你想要通信的 PID 的名称或将它们放在一个监督器下并使用监督器 API。 - José Valim
这两个回复都非常有帮助。我也不知道Supervisors,我只是一个初学者! - limp_chimp

1

你看过PubSub吗?唯一的限制是所有进程必须具有相同的名称https://hexdocs.pm/elixir/master/Registry.html#module-using-as-a-pubsub

{:ok, _} = Registry.start_link(:duplicate, Registry.PubSubTest)

# process 1
{:ok, _} = Registry.register(Registry.PubSubTest, "room_1", [])

# process 2
{:ok, _} = Registry.regiser(Registry.PubSubTest, "room_1", [])

Registry.dispatch(Registry.PubSubTest, "room_1", fn entries ->
 for {pid, _} <- entries, do: send(pid, {:broadcast, "world"})
end)
#=> :ok

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接