Можно ли отправить сообщение всем дочерним процессам в elixir / erlang?

Представим, что я создаю несколько дочерних процессов в 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 в какой-либо структуре данных и перебрать ее, чтобы сделать это, но если есть какой-то канонический способ сделать это, кажется, что это будет более эффективным и менее подверженным ошибкам.


person limp_chimp    schedule 24.01.2018    source источник


Ответы (2)


Поскольку вы используете 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. Есть способы, если вы создадите их от супервизора, но не для произвольных случаев.

person Dogbert    schedule 24.01.2018
comment
Проблема с этим подходом в том, что ссылки являются двунаправленными. Таким образом, если вы запускаете Child под супервизором, тогда супервизор, который является родителем, также будет в списке и получать сообщения. Всегда лучше хранить имена PID, с которыми вы хотите поговорить, или поместить их в супервизор и использовать Supervisor API. - person José Valim; 24.01.2018
comment
Оба ответа очень полезны. Про Супервайзеров тоже не знала - только новичок! - person limp_chimp; 24.01.2018

ты смотрел 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
person rurkss    schedule 12.02.2018