I’m watching a video on Go Concurrency Patterns. Here Rob Pike is discussing the Replication pattern:
The pattern is to launch multiple Go routines with the same function and return when you receive the first result.
I’m trying to accomplish the same result using Elixir Tasks. Here is my approach recursively using Task.yield_many/1
:
# task_await_one.exs
defmodule TaskTwo do
def run() do
tasks_for_a =
for i <- 1..10 do
Task.async(fn ->
Process.sleep(Enum.random(1000..5000))
IO.puts("Delivering result A from #{i}")
i
end)
end
await_first(tasks_for_a)
end
defp await_first(tasks) do
IO.puts("Trying...")
task_results = Task.yield_many(tasks, 100)
case Enum.find(task_results, fn {_, result} -> result != nil end) do
nil ->
# No tasks have completed yet, recurse with the same tasks
await_first(tasks)
{_completed_task, {:ok, result}} ->
# We got a result from one of the tasks
# Now, shut down all tasks
Enum.each(tasks, &Task.shutdown(&1, :brutal_kill))
result
end
end
end
{time, result} = :timer.tc(fn -> TaskTwo.run() end)
IO.puts(result)
IO.puts("Execution time: #{time / 1_000_000} seconds")
Does anyone have any ways I could improve my approach? I’m sure I could return more quickly if I wrote my own receive
function.
My next iteration based on the video would be to start tasks for three different functions and return the result as soon as each function returned one result. Something like this:
await_first_of_many(tasks_for_a, tasks_for_b, tasks_for_c)