简体   繁体   English

CancellationTokenSource.Cancel 抛出异常

[英]CancellationTokenSource.Cancel is throwing an exception

As I read the documentation CancellationTokenSource.Cancel is not supposed to throw an exception.当我阅读文档 CancellationTokenSource.Cancel 不应该抛出异常。
CancellationTokenSource.Cancel CancellationTokenSource.Cancel

Below the call to cts.Cancel();下面调用 cts.Cancel(); is causing (not throwing) a OperationCanceledException.导致(不抛出)OperationCanceledException。
I am very confident of that as if I comment that line out then that last OperationCanceledException is not thrown.我对此非常有信心,就好像我注释掉那行一样,最后一个 OperationCanceledException 不会被抛出。

With cts.Cancel line active the line that is throwing the exception is t2.Wait(token);随着 cts.Cancel 行处于活动状态,抛出异常的行是 t2.Wait(token);
And if I have a delay in cts.Cancel();如果我有延迟 cts.Cancel(); then t2.Wait(token);然后 t2.Wait(token); does not throw the exception as soon as the line is called.不会在调用该行时立即抛出异常。
t2.Wait(token); t2.等待(令牌); only throws that exception when cts.Cancel() is run.仅在 cts.Cancel() 运行时抛出该异常。
Is that proper behavior?这是正确的行为吗?
If it is consistent then I can live with it but I don't want cts.Cancel to cause an exception.如果它是一致的,那么我可以接受它,但我不希望 cts.Cancel 导致异常。
I am clearly confused - I just want to understand the behavior so I can feel comfortable taking this to a production environment.我显然很困惑 - 我只是想了解这种行为,所以我可以放心地将其带到生产环境中。
Right now I am doing this with a BackGroundWorker and I thought I could make it easier follow and maintain using wait and cancel.现在我正在使用 BackGroundWorker 执行此操作,我认为我可以使用等待和取消使其更容易跟踪和维护。

if (token.IsCancellationRequested || ctr == 100000000) if (token.IsCancellationRequested || ctr == 100000000)
still throws on ctr == 100000000仍然抛出 ctr == 100000000
I still see that inner OperationCanceledException caught and outer (lower) is not thrown at all.我仍然看到内部 OperationCanceledException 被捕获而外部(较低)根本没有被抛出。

Is there something wrong with this code?这段代码有什么问题吗?
Or is that how it is supposed to work?或者这是它应该如何工作?

Without the try catch in Task t2 = Task.Run I was getting an uncaught exception was thrown.如果没有在 Task t2 = Task.Run 中尝试捕获,我会抛出一个未捕获的异常。
I thought that would have been caught by the try catch on t2.Wait but one question at a time.我认为这会被 t2.Wait 上的 try catch 捕获,但一次一个问题。

This is console app on .NET 4.5.这是 .NET 4.5 上的控制台应用程序。

static void Main(string[] args)
{
    CancellationTokenSource cts = new CancellationTokenSource();
    CancellationToken token = cts.Token;

    Task.Run(() =>
    {
        Thread.Sleep(1000);
        cts.Cancel();  // this is thowing an exception that is caught on the last catch (OperationCanceledException Ex)
        // according to the documentation this is not supposed
        // if I comment out the cts.Cancel(); then the exeption is not thrown
        if (token.IsCancellationRequested)
            Console.WriteLine("Cancellation requested in Task {0}.",
                                Task.CurrentId);
        else
            Console.WriteLine("Cancellation Not requested in Task {0}.",
                                Task.CurrentId);

    }, token);

    //tried this but did not help
    //Task.Run(() =>
    //{
    //    //Thread.Sleep(1000);
    //    if (token.IsCancellationRequested)
    //        Console.WriteLine("Cancellation requested in Task {0}.",
    //                            Task.CurrentId);
    //}, token);
    //Thread.Sleep(1000);
    ////cts.Cancel();


    Task t2 = Task.Run(() =>
    {
        try
        {
            Console.WriteLine("Task t2 started Int32.MaxValue = " + Int32.MaxValue.ToString());
            Thread.Sleep(4000);
            for (int ctr = 0; ctr < Int32.MaxValue; ctr++)
            {
                if (ctr < 100 || ctr % 100000000 == 0)
                {
                    Console.WriteLine(ctr.ToString());

                }
                if (token.IsCancellationRequested || ctr == 100000000)  //  || ctr == 100000000
                {
                    Console.WriteLine("ThrowIfCancellationRequested in t2 Task  {0}.",
                                Task.CurrentId);
                    throw new OperationCanceledException(token);
                    //token.ThrowIfCancellationRequested();
                }
            }
            Console.WriteLine("Task {0} finished.",
                            Task.CurrentId);
        }
        catch (OperationCanceledException Ex)
        {
            //Console.WriteLine(Ex.ToString());
            Console.WriteLine("OperationCanceledException in Task t2 {0}: The operation was cancelled.",
                                Task.CurrentId);
        }
        catch (Exception Ex)
        {
            Console.WriteLine("Task t2 = Task.Run Exception Ex" + Ex.Message);
        }               
    });
    try
    {
        Console.WriteLine("t2.Wait a");
        t2.Wait(token);
    }
    catch (AggregateException e)
    {
        Console.WriteLine("AggregateException");
        foreach (var v in e.InnerExceptions)
            Console.WriteLine(e.Message + " " + v.Message);
    }
    catch (OperationCanceledException Ex)
    {
        //Console.WriteLine(Ex.ToString());
        Console.WriteLine("OperationCanceledException in Task {0}: The operation was cancelled.",
                            t2.Id);
    }
    catch (Exception Ex)
    {
        Console.WriteLine(Ex.ToString());
    }
    Console.WriteLine("end");
    Console.ReadLine();
}

Yes, this is the expected behavior: The overload of Task.Wait that takes a cancellation token waits until:是的,这是预期的行为:接受取消标记的Task.Wait的重载一直等到:

  • The task you are waiting on completes您正在等待的任务完成
  • The passed in cancellation token is cancelled.传入的取消令牌被取消。

In the latter case, when Task.Wait observes that the passed in token has been cancelled, it throws an 'OperationCancelledException'.在后一种情况下,当Task.Wait观察到传入的令牌已被取消时,它会抛出一个“OperationCancelledException”。 Here's the call stack when the exception is triggered in your case这是在您的情况下触发异常时的调用堆栈

    mscorlib.dll!System.Threading.CancellationToken.ThrowOperationCanceledException() Line 505 + 0x4d bytes C#
    mscorlib.dll!System.Threading.ManualResetEventSlim.Wait(int millisecondsTimeout, System.Threading.CancellationToken cancellationToken) Line 642 C#
    mscorlib.dll!System.Threading.Tasks.Task.SpinThenBlockingWait(int millisecondsTimeout, System.Threading.CancellationToken cancellationToken) Line 3284 + 0xf bytes  C#
    mscorlib.dll!System.Threading.Tasks.Task.InternalWait(int millisecondsTimeout, System.Threading.CancellationToken cancellationToken) Line 3223 + 0x10 bytes C#
    mscorlib.dll!System.Threading.Tasks.Task.Wait(int millisecondsTimeout, System.Threading.CancellationToken cancellationToken) Line 3129 + 0xc bytes  C#
    mscorlib.dll!System.Threading.Tasks.Task.Wait(System.Threading.CancellationToken cancellationToken) Line 3064 + 0xe bytes   C#
>   ConsoleApplication1.exe!ConsoleApplication1.Program.Main(string[] args) Line 82 + 0x15 bytes    C#

As to your second part of the question: If you - removed the try/catch within t2 and - you never cancelled the token,至于问题的第二部分:如果您 - 在 t2 内删除了 try/catch 并且 - 您从未取消过令牌,

then you would end up landing in your outer AggregateException handler (since the call to t2.Wait would throw an AggregateException with one inner exception which would be the OperationCanceledException that you threw.那么你最终会登陆你的外部AggregateException处理程序(因为对t2.Wait的调用会抛出一个带有一个内部异常的AggregateException ,这将是你抛出的OperationCanceledException

I might be wrong about this but the reason that you are getting this exception is because you are cancelling the same task in which you have put the cancel statement.我可能对此有误,但您收到此异常的原因是因为您取消了放置取消语句的同一任务。 The operationCanceledException MSDN documentation says the following :- operationCanceledException MSDN 文档说明如下:-

The exception that is thrown in a thread upon cancellation of an operation that the thread was executing.取消线程正在执行的操作时在线程中抛出的异常。

Can you try modifying the first task invoke to the following and check if you still get exception :-您可以尝试将第一个任务调用修改为以下内容并检查是否仍然出现异常:-

Task.Run(() =>
    {
        Thread.Sleep(1000);
        if (token.IsCancellationRequested)
            Console.WriteLine("Cancellation requested in Task {0}.",
                                Task.CurrentId);
    }, token);
cts.Cancel();

A helpful link for taskCancellationSource :- taskCancellationSource 的有用链接:-

http://johnbadams.wordpress.com/2012/03/10/understanding-cancellationtokensource-with-tasks/ http://johnbadams.wordpress.com/2012/03/10/understanding-cancellationtokensource-with-tasks/

t2.Wait(token); should throw after about 1sec because token is cancelled.应该在大约 1 秒后抛出,因为token被取消。

The line线

cts.Cancel();

cannot possibly have thrown if "Cancellation requested in Task {0}."如果"Cancellation requested in Task {0}."不可能抛出"Cancellation requested in Task {0}." was printed to the console as you said it was.正如你所说的那样打印到控制台。

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

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