简体   繁体   English

执行外部进程时,超时不适用于 F# Async.RunSynchronously

[英]Timeout doesn't work for F# Async.RunSynchronously when executing an external process

Starting a potentially slow or hanging process with the code below, will not result in a timeout:使用下面的代码启动一个可能很慢或挂起的过程不会导致超时:

open System.Diagnostics

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

The workaround is of course to use a timeout in the call to WaitForExit , but I'm curious about why the Async.RunSynchronously timeout doesn't work in this specific case.解决方法当然是在调用WaitForExit时使用超时,但我很好奇为什么Async.RunSynchronously超时在这种特定情况下不起作用。

The way cancellation in F# asynchronous workflows works is that it is collaborative - it will never forcefully kill the thread. F# 异步工作流的取消方式是协作的——它永远不会强行终止线程。 It propagates the cancellation token automatically, passes it to operations that support that and checks for cancellation every time the F# async workflow infrastructure gets a chance (typically before running code using let! and after it completes).它会自动传播取消令牌,将其传递给支持该令牌的操作,并在每次 F# 异步工作流基础架构有机会时检查取消(通常在使用let!运行代码之前和完成之后)。 This means that blocking code is not cancelled.这意味着阻塞代码不会被取消。 A smaller example that illustrates this would be:一个更小的例子可以说明这一点:

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

If you run this and cancel it immediately, it will print "Going to sleep" and then you'll only get "All completed" after 10 seconds - because the Thread.Sleep call is synchronous.如果你运行它并立即取消它,它会打印“Going to sleep”,然后你只会在 10 秒后得到“All completed”——因为Thread.Sleep调用是同步的。

If you want to kill the process when the workflow is cancelled, you should be able to explicitly register a cancellation handler and use the Kill method to stop the process - I have not tested this (I don't have a suitable slow process around,): but something like this should do the trick:如果你想在工作流被取消时终止进程,你应该能够显式注册一个取消处理程序并使用Kill方法来停止进程 - 我没有测试过这个(我没有合适的慢速进程, ):但是这样的事情应该可以解决问题:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM