为何这段F# Async异步代码未按预期在1秒超时后终止?
问题原因分析
Async.StartChild的timeout参数使用逻辑错误:Async.StartChild返回的是Async<Async<'T>>类型,你写的let! child = Async.StartChild (action, timeout)里的timeout仅限制等待子工作流启动完成的时长,而后续return! child等待子工作流运行结束的步骤没有任何超时限制。你的示例中forever启动无任何阻塞,启动步骤瞬间完成,timeout参数完全没有生效,后续等待forever运行结束的步骤会永远阻塞。- 自定义
Async.forever不支持协作式取消:F#异步采用协作式取消模型,需要工作流主动响应取消信号才能终止。你用Async.FromContinuations ignore实现的forever直接丢弃了所有回调函数,也没有监听取消令牌,就算后续收到取消信号也不会终止运行。
修正方案
首先重写withTimeout实现,将超时逻辑作用在等待子工作流运行结束的阶段,同时利用Async.Choice在触发超时时自动取消未完成的子工作流:
module Async = let withTimeout timeoutMs action = async { // 超时任务:指定时间后返回超时错误 let timeoutWorkflow = async { do! Async.Sleep timeoutMs return Error (System.TimeoutException()) } // 原工作流:正常运行返回成功结果 let mainWorkflow = async { let! result = action return Ok result } // 等待任意一个工作流先完成,未完成的工作流会自动收到取消信号 let! firstCompleted = Async.Choice [ mainWorkflow; timeoutWorkflow ] match firstCompleted with | Ok res -> return res | Error ex -> return! Async.Raise ex }
其次修改Async.forever实现,使其支持取消:F#异步的所有原生let!/do!绑定点都会自动检查取消令牌,只要工作流包含异步绑定点就可以自动响应取消:
module Async = // 支持取消的永久运行异步工作流 let forever = async { while true do // 异步等待会自动检查取消令牌,收到取消信号时会终止工作流 do! Async.Sleep(10) }
完整可运行的测试代码如下:
module Async = let withTimeout timeoutMs action = async { let timeoutWorkflow = async { do! Async.Sleep timeoutMs return Error (System.TimeoutException()) } let mainWorkflow = async { let! result = action return Ok result } let! firstCompleted = Async.Choice [ mainWorkflow; timeoutWorkflow ] match firstCompleted with | Ok res -> return res | Error ex -> return! Async.Raise ex } let forever = async { while true do do! Async.Sleep(10) } async { printfn "Started... " try do! Async.forever |> Async.withTimeout 1000 with :? System.TimeoutException -> printfn "Timed out after 1000ms" printfn "Finished. " } |> Async.RunSynchronously
运行后会输出符合预期的结果:
Started... Timed out after 1000ms Finished.
内容的提问来源于stack exchange,提问作者sdgfsdh
相关产品推荐
相关产品推荐

