首页 > 解决方案 > 执行外部进程时超时不适用于 F# Async.RunSynchronously

问题描述

使用下面的代码启动一个可能很慢或挂起的过程不会导致超时:

open System.Diagnostics

[<EntryPoint>]
let main argv =
    Async.RunSynchronously(
        async {
            use proc = Process.Start(@"SlowProcess.exe")
            proc.WaitForExit()
        }, timeout = 2000)
    0

解决方法当然是在调用中使用超时WaitForExit,但我很好奇为什么Async.RunSynchronously超时在这种特定情况下不起作用。

标签: .netf#

解决方案


F# 异步工作流中取消的工作方式是协作的——它永远不会强行终止线程。它会自动传播取消令牌,将其传递给支持该令牌的操作,并在每次 F# 异步工作流基础架构有机会时检查取消(通常在使用运行代码之前let!和完成之后)。这意味着阻塞代码不会被取消。一个更小的例子可以说明这一点:

let work = async {
    try
      printfn "Going to sleep"
      Thread.Sleep(10000)
      printfn "Waking up"  
    finally
      printfn "All completed"
  }

如果你运行它并立即取消它,它会打印“Going to sleep”,然后你只会在 10 秒后得到“All completed”——因为Thread.Sleep调用是同步的。

如果你想在工作流被取消时终止进程,你应该能够显式注册一个取消处理程序并使用该Kill方法来停止进程 - 我没有测试过这个(我没有合适的慢进程!) ,但这样的事情应该可以解决问题:

let work = async {
    let! tok = Async.CancellationToken
    use proc = Process.Start(@"SlowProcess.exe")
    tok.Register(fun () -> proc.Kill()) |> ignore
    proc.WaitForExit()
  }

推荐阅读