【问题标题】:Why Async.StartChild does not take CancellationToken?为什么 Async.StartChild 不使用 CancellationToken?
【发布时间】:2018-10-20 20:39:44
【问题描述】:

我很难理解Async.[StartChild|Start] API 设计。 我想要的是启动一个异步进程,它会根据到达 tcp 的命令读取一些 tcp 流并调用回调。

由于这个异步进程并没有真正返回任何单个值,我似乎应该使用Async.Start。在某些时候,我想“关闭”我的 tcp 客户端,并且 `Async.Start 需要 CancellationToken,这使我能够实现“关闭”。到目前为止一切顺利。

问题是,我想知道 tcp 客户端何时完成取消。一旦请求取消,就会完成一些缓冲区刷新工作,所以我不想在 tcp 客户端完成清理之前终止应用程序。但是Async.Start 返回单位,这意味着我无法知道这样的异步过程何时完成。所以,看起来Async.StartChild 应该会有所帮助。我应该能够调用取消,当清理完成后,这个异步将调用链中的下一个继续(或抛出异常?)。但是...Async.StartChild 不带 CancellationToken,只有超时。

为什么Async.StartChild 只实现单例取消策略(超时)而不是公开更通用的方式(接受 CancellationToken)?

【问题讨论】:

    标签: asynchronous f#


    【解决方案1】:

    要回答问题的第一部分——如果你需要做一些清理工作,你可以把它放在finally 中,它会在工作流取消时被调用。例如:

    let work = 
      async {
        try
          printfn "first work"
          do! Async.Sleep 1000
          printfn "second work"
        finally
          printfn "cleanup" }
    

    假设您使用Async.Start 运行此程序,等待 500 毫秒然后取消计算:

    let cts = new System.Threading.CancellationTokenSource()
    Async.Start(work, cts.Token)
    System.Threading.Thread.Sleep(500)
    cts.Cancel()
    

    输出将是“第一个工作,清理”。如您所见,取消计算将运行所有finally 子句。

    要回答问题的第二部分 - 如果您需要等到工作完成,您可以使用RunSynchronously(但是,如果您仍然阻塞,也许您实际上并不需要异步工作流......) .

    下面启动一个后台进程,500ms后取消主工作,然后同步启动主工作:

    let cts = new System.Threading.CancellationTokenSource()
    
    async {
      do! Async.Sleep(500)
      cts.Cancel() } |> Async.Start
    
    try Async.RunSynchronously(work, cancellationToken=cts.Token)
    with :? System.OperationCanceledException -> ()
    printfn "completed"
    

    这会打印“第一个工作,清理,完成” - 如您所见,RunSynchronously 调用被阻止,直到工作被取消。

    【讨论】:

    • 啊哈,现在我记得CancellationToken.Register 是从CancellationTokenSource.Cancel 调用的同步。所以我所有的取消回调,例如Async.OnCancel等都会被同步调用,我不必担心杀死正在进行的后台线程。
    猜你喜欢
    • 1970-01-01
    • 2014-08-16
    • 2021-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多