我有几个异步运行的任务。根据输入,一个或多个可能会运行很长时间,但只有一个任务将返回:success消息。
slowtask = Task.async(slow())
fasttask = Task.async(fast())
如何捕获上述两个任务中的第一个完成,而不必等待另一个?我已经尝试了Task.find/2
,但由于它是用枚举实现的,所以它似乎在找到ref / message之前等待所有退出信号。我的另一个想法是在Stream.cycle
中对此进行轮询,忽略仍然存在的任务并捕获已退出的任务。看来这种灵丹妙药不喜欢以这种方式进行民意调查。
答案 0 :(得分:8)
还没有简单的方法在Elixir上做到这一点。您最好的选择是,如果您只是在等待给定流程中的那些消息,则是这样的:
defmodule TaskFinder do
def run do
task1 = Task.async fn -> :timer.sleep(1000); 1 end
task2 = Task.async fn -> :timer.sleep(5000); 2 end
await [task1, task2]
end
# Be careful, this will receive all messages sent
# to this process. It will return the first task
# reply and the list of tasks that came second.
def await(tasks) do
receive do
message ->
case Task.find(tasks, message) do
{reply, task} ->
{reply, List.delete(tasks, task)}
nil ->
await(tasks)
end
end
end
end
IO.inspect TaskFinder.run
请注意,您也可以使用此模式在GenServer中生成任务,并使用Task.find/2
查找匹配的模式。我还将此示例添加到Elixir文档中。
答案 1 :(得分:3)
要获得第一个结果,您应该等待消息,然后将消息传递给Task.find/2
,并处理{task_result, task}
形式的第一个结果。
defmodule Tasks do
def run do
:random.seed(:os.timestamp)
durations = Enum.shuffle(1..10)
Enum.map(durations, fn(duration) -> Task.async(fn -> run_task(duration) end) end)
|> get_first_result
|> IO.inspect
end
defp get_first_result(tasks) do
receive do
msg ->
case Task.find(tasks, msg) do
{result, _task} ->
# got the result
result
nil ->
# no result -> continue waiting
get_first_result(tasks)
end
end
end
defp run_task(1) do
:success
end
defp run_task(duration) do
:timer.sleep(duration * 100)
:ok
end
end
如果"主人"进程是GenServer
,您应该从Task.find/2
内调用handle_info/2
,而不是运行此递归循环。
答案 2 :(得分:1)
根据Jose Valim的回答,这是我用来匹配回复的回复:
def run do
task1 = Task.async(fn -> :timer.sleep(10000); :slow end)
task2 = Task.async(fn -> :timer.sleep(2000); :fail end)
task3 = Task.async(fn -> :timer.sleep(1000); :fail end)
await([task1, task2, task3])
end
def await(tasks) do
receive do
message ->
case Task.find(tasks, message) do
{:fail, task} ->
await(List.delete(tasks, task))
{reply, _task} ->
reply
nil ->
await(tasks)
end
end
end
这允许我匹配第一个函数来返回除了:fail atom之外的其他东西,然后给我回复。这是否有效,因为receive / 1只是等待任何消息出现?