F#异步方式取消工程? [英] F# How Async<'T> cancellation works?

查看:73
本文介绍了F#异步方式取消工程?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对TPL在C#中完成异步取消的方式感到非常满意,但是在F#中我有点困惑.显然,通过调用Async.CancelDefaultToken()足以取消传出的Async<'T>操作.但是它们并没有像我预期的那样被取消,它们只是……消失了……我无法正确检测到取消并正确地拆除了堆栈.

I was pretty comfortable with how async cancellations where done in C# with the TPL, but I am a little bit confused in F#. Apparently by calling Async.CancelDefaultToken() is enough to cancel outgoing Async<'T> operations. But they are not cancelled as I expected, they just... vanishes... I cannot detect properly the cancellation and tear down the stack properly.

例如,我有依赖于使用TPL的C#库的以下代码:

For example, I have this code that depends on a C# library that uses TPL:

type WebSocketListener with
  member x.AsyncAcceptWebSocket = async {
    let! client = Async.AwaitTask <| x.AcceptWebSocketAsync Async.DefaultCancellationToken
    if(not(isNull client)) then
        return Some client
    else 
        return None
  }

let rec AsyncAcceptClients(listener : WebSocketListener) =
  async {
    let! result = listener.AsyncAcceptWebSocket
    match result with
        | None -> printf "Stop accepting clients.\n"
        | Some client ->
            Async.Start <| AsyncAcceptMessages client
            do! AsyncAcceptClients listener
  }

取消传递给x.AcceptWebSocketAsyncCancellationToken时,返回null,然后AsyncAcceptWebSocket方法返回None.我可以使用断点来验证这一点.

When the CancellationToken passed to x.AcceptWebSocketAsync is cancelled, returns null, and then AsyncAcceptWebSocket method returns None. I can verify this with a breakpoint.

但是,AsyncAcceptClients(调用方)永远不会获得该None值,该方法只是结束,并且"Stop accepting clients.\n"永远不会显示在控制台上.如果我将所有内容包装在try\finally中:

But, AsyncAcceptClients (the caller), never gets that None value, the method just ends, and "Stop accepting clients.\n" is never displayed on the console. If I wrap everything in a try\finally :

let rec AsyncAcceptClients(listener : WebSocketListener) =
  async {
    try
        let! result = listener.AsyncAcceptWebSocket
        match result with
            | None -> printf "Stop accepting clients.\n"
            | Some client ->
                Async.Start <| AsyncAcceptMessages client
                do! AsyncAcceptClients listener
   finally
        printf "This message is actually printed"
  }

然后,当listener.AsyncAcceptWebSocket返回None时,我在finally中放置的内容将被执行,但是在match中的代码仍然没有. (实际上,它为每个连接的客户端在finally块上打印一次消息,所以也许我应该采用迭代方法?)

Then what I put in the finally gets executed when listener.AsyncAcceptWebSocket returns None, but the code I have in the match still doesn't. (Actually, it prints the message on the finally block once for each connected client, so maybe I should move to an iterative approach?)

但是,如果我使用自定义的CancellationToken而不是Async.DefaultCancellationToken,则一切正常,并且"Stop accepting clients.\n"消息将显示在屏幕上.

However, if I use a custom CancellationToken rather than Async.DefaultCancellationToken, everything works as expected, and the "Stop accepting clients.\n" message is print on screen.

这是怎么回事?

推荐答案

这个问题有两件事:

  • 首先,当F#中发生取消时,AwaitTask不返回null,而是任务抛出OperationCanceledException异常.因此,您没有获得None值,而是得到了一个异常(然后F#也运行了finally块).

  • First, when a cancellation happens in F#, the AwaitTask does not return null, but instead, the task throws OperationCanceledException exception. So, you do not get back None value, but instead, you get an exception (and then F# also runs your finally block).

令人困惑的是,取消是一种特殊的异常,无法在async块内的用户代码中处理-一旦取消计算,就不能取消它,并且它总是停止(您可以这样做finally中的清除).您可以解决此问题(请参见此SO答案),但这可能会导致意外的情况.

The confusing thing is that cancellation is a special kind of exception that cannot be handled in user code inside the async block - once your computation is cancelled, it cannot be un-cancelled and it will always stop (you can do cleanup in finally). You can workaround this (see this SO answer) but it might cause unexpected things.

第二,我不会使用默认的取消令牌-所有异步工作流程都共享该令牌,因此它可能会执行意外的操作.相反,您可以使用Async.CancellationToken,它使您可以访问当前的取消令牌(F#自动为您传播-因此您不必像在C#中那样手动传递它).

Second, I would not use default cancellation token - that's shared by all async workflows and so it might do unexpected things. You can instead use Async.CancellationToken which gives you access to a current cancellation token (which F# automatically propagates for you - so you do not have to pass it around by hand as you do in C#).

编辑:阐明了F#异步处理取消异常的方式.

EDIT: Clarified how F# async handles cancellation exceptions.

这篇关于F#异步方式取消工程?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆