Elixir中Task.await触发FunctionClauseError问题的解决求助
解决Task.async_stream配合Task.await触发FunctionClauseError的问题
问题重现
尝试通过子进程批量检查质数,调用Task.await时触发FunctionClauseError,核心代码及报错如下:
defmodule LeaderWorker do def start() do children = [{Task.Supervisor, name: Task.SomeThingSupervisor, }] Supervisor.start_link(children, strategy: :one_for_one) end def run(number_list, num_workers) do results = number_list |> Task.async_stream(&check_prime/1, max_concurrency: num_workers) |> Enum.map(&Task.await/1) IO.inspect(results) end def check_prime(number) do prime = is_prime(number) IO.inspect({number, prime}) {number, prime} end def is_prime(number) when number <= 1 do false end def is_prime(number) do not Enum.any?(2..(div(number, 2)), &(&1 != 1 and rem(number, &1) == 0)) end end LeaderWorker.start() number_list = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] LeaderWorker.run(number_list, 4)
报错信息:
elixir .\clean.ex {2, false} {3, true} {4, false} {5, true} ** (FunctionClauseError) no function clause matching in Task.await/2 The following arguments were given to Task.await/2: # 1 {:ok, {2, false}} # 2 5000 Attempted function clauses (showing 1 out of 1): def await(%Task{ref: ref, owner: owner} = task, timeout) when timeout == :infinity or is_integer(timeout) and timeout >= 0 (elixir 1.16.2) Task.await/2 (elixir 1.16.2) lib/enum.ex:1708: anonymous fn/3 in Enum.map/2 (elixir 1.16.2) lib/enum.ex:4396: anonymous fn/3 in Enum.map/2 (elixir 1.16.2) lib/task/supervised.ex:386: Task.Supervised.stream_deliver/7 (elixir 1.16.2) lib/enum.ex:4396: Enum.map/2 clean.ex:14: LeaderWorker.run/2 clean.ex:36: (file)
错误原因
Task.async_stream的返回值是一个包含结果元组的流,每个元素格式为{:ok, result}(任务失败时为{:error, reason}),而Task.await要求接收的是Task结构体,直接将{:ok, result}传给Task.await必然不匹配函数子句,触发错误。
同时原代码的is_prime函数存在逻辑错误:对于数字2,当前实现会错误返回false,需要修正质数判断逻辑。
修复后的代码
defmodule LeaderWorker do def start() do children = [{Task.Supervisor, name: Task.SomeThingSupervisor}] Supervisor.start_link(children, strategy: :one_for_one) end def run(number_list, num_workers) do results = number_list |> Task.async_stream(&check_prime/1, max_concurrency: num_workers) # 直接提取async_stream返回的结果,无需调用Task.await |> Enum.map(fn {:ok, result} -> result end) IO.inspect(results) end def check_prime(number) do prime = is_prime(number) {number, prime} end def is_prime(number) when number <= 1, do: false def is_prime(2), do: true # 单独处理最小质数2 def is_prime(number) when number even?, do: false # 偶数直接返回false(除2外) def is_prime(number) do # 优化判断范围到平方根,提升大数字的计算性能 max_divisor = :math.sqrt(number) |> floor() not Enum.any?(3..max_divisor//2, &(rem(number, &1) == 0)) end end LeaderWorker.start() number_list = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] LeaderWorker.run(number_list, 4)
关键修复点
- 移除多余的
Task.await调用:Task.async_stream内部已经完成任务等待和结果收集,直接从{:ok, result}元组中提取结果即可。 - 修正质数判断逻辑:
- 单独处理数字2,确保返回正确的
true - 偶数(除2外)直接返回
false,减少无效计算 - 将判断范围缩小到数字的平方根,大幅提升大数字的质数判断效率
- 单独处理数字2,确保返回正确的
运行修复后的代码,会输出正确的结果列表:
[{2, true}, {3, true}, {4, false}, {5, true}, {6, false}, {7, true}, {8, false}, {9, false}, {10, false}, {11, true}]
内容的提问来源于stack exchange,提问作者Kiubert
相关产品推荐
相关产品推荐

