在多线程elixir中启动多个进程

时间:2017-10-31 12:04:22

标签: multithreading elixir

我只是对数据库中的服务器列表进行了一些操作

list_servers()
|> Enum.map(fn(server) ->
  go_through_and_email(server)
end)

我唯一的问题是,是否有可能分别为每个服务器启动每个go_through_email(server)进程?现在,它完成了第一个,然后进入第二个。我们可以并行运行它们吗?因为list_servers可能有100或100个对象。

我不想使用Task.awaitTask.async,还有其他选择可以探索吗?

4 个答案:

答案 0 :(得分:1)

您可以使用Task.async_stream/3。它允许您传递一个枚举并在它们上运行一个函数,并提供一个选项,使用max_concurrency选项限制并行运行的最大任务数。默认timeout是5000毫秒,你可能想要指定一个更大的。{/ p>

这是一个小例子:

1..20
|> Task.async_stream(fn x ->
  :timer.sleep(1000)
  x
end, max_concurrency: 4, timeout: 10_000)
|> Enum.each(&IO.inspect/1)

如果你运行它,你应该看到{:ok, 1}{:ok, 20}每秒打印4个批次。整个事情应该在5秒内结束,正如预期的那样,在20个项目中以4个批次进行1秒钟睡眠。

答案 1 :(得分:1)

您可以做的一件事是使用import random import pygame as pg from pygame.math import Vector2 class Projectile(pg.sprite.Sprite): def __init__(self, pos, game_area): super().__init__() self.image = pg.Surface((5, 5)) self.image.fill(pg.Color('aquamarine2')) self.rect = self.image.get_rect(center=pos) self.vel = Vector2(2, 0).rotate(random.randrange(360)) self.pos = Vector2(pos) self.game_area = game_area def update(self): self.pos += self.vel self.rect.center = self.pos if not self.game_area.contains(self.rect): self.kill() def main(): screen = pg.display.set_mode((640, 480)) game_area = pg.Rect(60, 60, 520, 360) game_area_color = pg.Color('aquamarine2') clock = pg.time.Clock() all_sprites = pg.sprite.Group(Projectile(game_area.center, game_area)) done = False while not done: for event in pg.event.get(): if event.type == pg.QUIT: done = True all_sprites.add(Projectile(game_area.center, game_area)) all_sprites.update() screen.fill((30, 30, 30)) all_sprites.draw(screen) pg.draw.rect(screen, game_area_color, game_area, 2) pg.display.flip() clock.tick(60) if __name__ == '__main__': pg.init() main() pg.quit() ,但您不想使用任务模块。

另一件事是摆脱Task.start中的go_through_email(server)。假设Enum.map是数据库服务器,您应该创建一个将与数据库服务器通信的GenServers,并在server中向每个GenServer发送一条消息。

map

然后每个GenServer都会处理并行代码。当然,您需要准备好GenServers列表。这显然比明确使用Enum.map(list_of_gen_servers, &send_message_to_go_through_email(&1)) 更好。

修改

如果你想send / receive

任务模块会很棒。如果您经常访问数据库服务器并且允许完全控制通信模型应该如何,GenServers将是更好的选择。

答案 2 :(得分:1)

您也可以使用GenServer。如果将list_servers()和go_through_and_email()定义为辅助函数,您应该可以执行以下操作:

defmodule ExampleGenServer do
  use GenServer

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

  def go_through(name) do
    GenServer.cast(name, {:go_through, name})
  end

  ## Server API / Callbacks
  def init(initial_state) do
    {:ok, initial_state}
  end

  def handle_cast({:go_through, name}, state) do
    state = go_through_and_email(server)
    {:noreply, state}
  end

  def handle_info({:some_info}, state) do
    # stuff
    {:noreply, state}
  end

  ## Helper functions
  defp list_servers() do
    # stuff
  end

  defp go_through_and_email() do
    # stuff
  end
end

然后你可以在一个循环中创建新的GenServer实例:

list_servers()
|> Enum.map(fn(server) ->
  ExampleGenserver.start_link(server)
end)

我在休息期间很快就做到了,所以我可能会错过/弄乱一些东西;) 如果您需要有关GenServer的其他详细信息,请参阅我的Gist。它描述了GenServer客户端/服务器API,消息传递和arg传递;) 还有一件事,请记住GenServer的拆解! ;)

答案 3 :(得分:0)

您还可以将实际任务推迟到另一位主管。我无法确定您的案例中是否需要简单函数或gen服务器,但请查看simple_one_for_one supervisor

这种主管非常适合这种需求(发送电子邮件,如果我正确的话)。

PS。你没有说Task,但玩Task.async_stream和超时是另一个合适的选择。