为什么Async.StartChild不采取CancellationToken?

时间:2018-05-10 14:24:46

标签: asynchronous f#

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

由于这个异步进程并没有真正返回任何单个值,所以我似乎应该使用Async.Start。在某些时候我想“关闭”我的tcp客户端和`Async.Start采取CancellationToken,这使我能够实现'关闭'。到目前为止一直很好。

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

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

1 个答案:

答案 0 :(得分:4)

要回答问题的第一部分 - 如果您需要进行一些清理工作,可以将其放在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来电被阻止,直到工作被取消。