如何捕获CancellationToken.Register回调异常? [英] How do you catch CancellationToken.Register callback exceptions?

查看:437
本文介绍了如何捕获CancellationToken.Register回调异常?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用异步I / O与HID设备进行通信,并且我想在超时时引发可捕获的异常。我有以下读取方法:

 公共异步任务< int> Read(byte [] buffer,int?size = null)
{
size = size ?? buffer.Length;

using(var cts = new CancellationTokenSource())
{
cts.CancelAfter(1000);
cts.Token.Register(()=> {throw new TimeoutException( read timeout);},true);
try
{
var t = stream.ReadAsync(buffer,0,size.Value,cts.Token);
等待t;
返回t.Result;
}
catch(异常例外)
{
Debug.WriteLine( exception);
返回0;
}
}
}

从令牌中抛出的异常回调没有被任何try / catch块捕获,我不确定为什么。我以为它会在等待中抛出,但事实并非如此。有没有办法捕获此异常(或使其可以被Read()的调用者捕获)?



EDIT:
因此,我在 msdn ,并说委托产生的任何异常都会传播到该方法调用之外。



我不确定传播出该方法的含义此方法调用,因为即使我将.Register()调用移到try块中,仍然没有捕获到异常。

解决方案

< blockquote>

编辑:所以我重新阅读了msdn上的文档,它说:任何
委托生成的异常都会传播到该方法调用之外。



我不确定传播到此方法调用之外
是什么意思,因为即使我将.Register()调用移到try块中,
仍未捕获到异常。


这是什么意思取消回调的调用方(.NET运行时内部的代码)将不会尝试捕获您可能在此处引发的任何异常,因此它们将在回调被调用的任何堆栈帧和同步上下文上传播到回调之外上。这可能会使应用程序崩溃,因此您实际上应该处理回调中的所有非致命异常。 从事件处理程序的角度考虑。毕竟,可能在 ct.Register()中注册了多个回调,并且每个回调都可能抛出。那么应该传播哪个异常?



因此,不会捕获此类异常并将其传播到令牌的客户端端(例如,调用 CancellationToken.ThrowIfCancellationRequested )的代码。



这是抛出<$ c $的另一种方法c> TimeoutException ,如果您需要区分用户取消(例如停止按钮)和超时:

 公共异步任务< int>读取(字节[]缓冲区,整数?size = null,
CancellationToken userToken)
{
size = size? buffer.Length;

using(var cts = CancellationTokenSource.CreateLinkedTokenSource(userToken))
{
cts.CancelAfter(1000);
try
{
var t = stream.ReadAsync(buffer,0,size.Value,cts.Token);
试试
{
等待t;
}
catch(OperationCanceledException ex)
{
if(ex.CancellationToken == cts.Token)
throw new TimeoutException( read timeout,ex);
投掷;
}
返回t.Result;
}
catch(异常例外)
{
Debug.WriteLine( exception);
返回0;
}
}
}


I am using async I/O to communicate with an HID device, and I would like to throw a catchable exception when there is a timeout. I've got the following read method:

public async Task<int> Read( byte[] buffer, int? size=null )
{
    size = size ?? buffer.Length;

    using( var cts = new CancellationTokenSource() )
    {
        cts.CancelAfter( 1000 );
        cts.Token.Register( () => { throw new TimeoutException( "read timeout" ); }, true );
        try
        {
            var t =  stream.ReadAsync( buffer, 0, size.Value, cts.Token );
            await t;
            return t.Result;
        }
        catch( Exception ex )
        {
            Debug.WriteLine( "exception" );
            return 0;
        }
    }
}

The exception thrown from the Token's callback is not caught by any try/catch blocks and I'm not sure why. I assumed it would be thrown at the await, but it is not. Is there a way to catch this exception (or make it catchable by the caller of Read())?

EDIT: So I re-read the doc at msdn, and it says "Any exception the delegate generates will be propagated out of this method call."

I'm not sure what it means by "propagated out of this method call", because even if I move the .Register() call into the try block the exception is still not caught.

解决方案

EDIT: So I re-read the doc at msdn, and it says "Any exception the delegate generates will be propagated out of this method call."

I'm not sure what it means by "propagated out of this method call", because even if I move the .Register() call into the try block the exception is still not caught.

What this means is that the caller of your cancellation callback (the code inside .NET Runtime) won't make an attempt to catch any exceptions you may throw there, so they will be propagated outside your callback, on whatever stack frame and synchronization context the callback was invoked on. This may crash the application, so you really should handle all non-fatal exceptions inside your callback. Think of it as of an event handler. After all, there may be multiple callbacks registered with ct.Register(), and each might throw. Which exception should have been propagated then?

So, such exception will not be captured and propagated into the "client" side of the token (i.e., to the code which calls CancellationToken.ThrowIfCancellationRequested).

Here's an alternative approach to throw TimeoutException, if you need to differentiate between user cancellation (e.g., a "Stop" button) and a timeout:

public async Task<int> Read( byte[] buffer, int? size=null, 
    CancellationToken userToken)
{
    size = size ?? buffer.Length;

    using( var cts = CancellationTokenSource.CreateLinkedTokenSource(userToken))
    {
        cts.CancelAfter( 1000 );
        try
        {
            var t =  stream.ReadAsync( buffer, 0, size.Value, cts.Token );
            try
            {
                await t;
            }
            catch (OperationCanceledException ex)
            {
                if (ex.CancellationToken == cts.Token)
                    throw new TimeoutException("read timeout", ex);
                throw;
            }
            return t.Result;
        }
        catch( Exception ex )
        {
            Debug.WriteLine( "exception" );
            return 0;
        }
    }
}

这篇关于如何捕获CancellationToken.Register回调异常?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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