Pergunta

As I read the documentation CancellationTokenSource.Cancel is not supposed to throw an exception.
CancellationTokenSource.Cancel

Below the call to cts.Cancel(); is causing (not throwing) a OperationCanceledException.
I am very confident of that as if I comment that line out then that last OperationCanceledException is not thrown.

With cts.Cancel line active the line that is throwing the exception is t2.Wait(token);
And if I have a delay in cts.Cancel(); then t2.Wait(token); does not throw the exception as soon as the line is called.
t2.Wait(token); only throws that exception when cts.Cancel() is run.
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.
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.

if (token.IsCancellationRequested || ctr == 100000000)
still throws on ctr == 100000000
I still see that inner OperationCanceledException caught and outer (lower) is not thrown at all.

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.
I thought that would have been caught by the try catch on t2.Wait but one question at a time.

This is console app on .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();
}
Foi útil?

Solução 2

t2.Wait(token); should throw after about 1sec because token is cancelled.

The line

cts.Cancel();

cannot possibly have thrown if "Cancellation requested in Task {0}." was printed to the console as you said it was.

Outras dicas

Yes, this is the expected behavior: The overload of Task.Wait that takes a cancellation token waits until:

  • 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'. 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,

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.

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 :-

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 :-

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

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top