0

Trying to learn Task, Wait and Cancellation.
Started with what I thought was a simple sample from MSDN.
Task.Wait Method (CancellationToken)

As a console application this runs as expected.
That same code does not run as expected in WPF.
In WPF t2 Runs to completion - the line Task finished is printed in debug.
It hits the OperationCanceledException between 0 and 100000000 but t2 keeps on running.
(note had to change it to ctr < Int32.MaxValue or it would never finish)

On .NET 4.5 and also tried 4.51

public void TestCancel1()
{
    CancellationTokenSource cts = new CancellationTokenSource();
    CancellationToken token = cts.Token;

    Task.Run(() =>
    {
        cts.Cancel();
        if (token.IsCancellationRequested)
            Debug.WriteLine("Cancellation requested in Task {0}.",
                                Task.CurrentId);
    }, token);
    Task t2 = Task.Run(() =>
    {
        Debug.WriteLine(Int32.MaxValue.ToString());
        for (Int32 ctr = 0; ctr < Int32.MaxValue; ctr++)  // 
        {
            if (ctr % 100000000 == 0)
                Debug.WriteLine(ctr.ToString());
        }
        Debug.WriteLine("Task {0} finished.",
                            Task.CurrentId);
    });
    try
    {
        t2.Wait(token);
    }
    catch (OperationCanceledException)
    {
        Debug.WriteLine("OperationCanceledException in Task {0}: The operation was cancelled.", 
                         t2.Id);
    }
}

there have been question on the console app
here is the code

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

    Task.Run(() =>
    {
        Thread.Sleep(1000);  // change number and will get a differnt last ctr
        cts.Cancel();
        if (token.IsCancellationRequested)
            Console.WriteLine("Cancellation requested in Task {0}.",
                                Task.CurrentId);
    }, token);
    Task t2 = Task.Run(() =>
    {
        Console.WriteLine(Int32.MaxValue.ToString());
        for (int ctr = 0; ctr < Int32.MaxValue; ctr++)
        {
            Console.WriteLine(ctr.ToString());
            if (ctr % 100000000 == 0) Console.WriteLine(ctr.ToString());
        }
        Console.WriteLine("Task {0} finished.",
                            Task.CurrentId);
    });
    try
    {
        t2.Wait(token);
    }
    catch (OperationCanceledException)
    {
        Console.WriteLine("OperationCanceledException in Task {0}: The operation was cancelled.",
                            t2.Id);
    }
}

Added the ThrowIfCancellationRequested
I am getting an error on that line OperationCancelException was unhandled by user code

Task t2 = Task.Run(() =>
{
    Debug.WriteLine(Int32.MaxValue.ToString());
    for (Int32 ctr = 0; ctr < Int32.MaxValue; ctr++)  // 
    {                   
        if (ctr % 100000000 == 0)
        {
            Debug.WriteLine(ctr.ToString() + " " + token.IsCancellationRequested.ToString());
        }
        if (token.IsCancellationRequested)
            token.ThrowIfCancellationRequested();
    }
    Debug.WriteLine("Task {0} finished.",
                        Task.CurrentId);
}); //, token);

OK I know this is getting long
That sample from Microsoft is misleading at best - why would you have a sample for Cancel that does not really cancel
To verify why Rohit stated I added the following
And sure enough I could see some more passes before the press any key to continue

    catch (OperationCanceledException)
    {               
        Console.WriteLine("OperationCanceledException in Task {0}: The operation was cancelled.",
                            t2.Id);
        Thread.Sleep(10);
        Console.WriteLine("exit exception");
    }
paparazzo
  • 44,497
  • 23
  • 105
  • 176
  • please show how you tested in the console, as that is the one that is bugged, WPF acts correctly. – Scott Chamberlain Mar 16 '14 at 20:17
  • You have tagged your question `await`, but do not use `await` in your code. If you are actually blocking (`Wait`) on asynchronous code (`await`), then you're probably running into a [deadlock scenario I describe on my blog](http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html). – Stephen Cleary Mar 16 '14 at 20:37
  • @StephenCleary Sorry fixed that – paparazzo Mar 16 '14 at 20:49
  • @ScottChamberlain I put exactly what is in TestCancel1 in console Main . If you look at the link it states that it expects it to end at Cancel. If I debug every ctr then it gets 24. It is definitely exiting that (in console). – paparazzo Mar 16 '14 at 20:54
  • @Blam the point of the example from Microsoft is the Task runs forever, what you are "canceling" is the action of "waiting for the task to finish" not the task itself. – Scott Chamberlain Mar 16 '14 at 22:26
  • @ScottChamberlain That was clearly not clear to me. But it was good learning experience. – paparazzo Mar 17 '14 at 00:28

2 Answers2

2

It is likely your console test code that is bugged and your expectations are wrong. In the console application 2t keeps on running too but the program quit before t2 finished.

Cancellation token sources are "Cooperative cancelation" if you want t2 to end early you need to check inside t2 if the cancellation has happened yet.

Task t2 = Task.Run(() =>
{
    Debug.WriteLine(Int32.MaxValue.ToString());
    for (Int32 ctr = 0; ctr < Int32.MaxValue; ctr++)  // 
    {
        if (ctr % 100000000 == 0)
            Debug.WriteLine(ctr.ToString());
        token.ThrowIfCancellationRequested();
    }
    Debug.WriteLine("Task {0} finished.",
                        Task.CurrentId);
});
Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
  • Please see where I posted my console test. Please see my comment to the answer from Rohit. Unless I am missing something big that console app is terminating t2 (and the documentation from Microsoft link I provided indicates that is what is is supposed to do). – paparazzo Mar 16 '14 at 21:11
  • In WPF I am getting an unhandled exception on ThrowIfCancellationRequested(); (but it does terminate). And I even added a catch (Exception EX) – paparazzo Mar 16 '14 at 21:15
1

This is not related to WPF. Output will be same for Console application, WPF or WinForms.

As a console application this runs as expected. That same code does not run as expected in WPF. In WPF t2 Runs to completion - the line Task finished is printed in debug.

I suspect in console application you tested with ctr <= Int32.MaxValue that's why Finised is not printed because it runs infinitely and Finish never gets printed on console.


It hits the OperationCanceledException between 0 and 100000000 but t2 keeps on running.

Exception won't stop task t2 from executing to finish at all.

OperationCanceledException gets thrown on main thread because you ask main thread to wait on task t2 and pass cancellation token. So, with catching this exception your main thread is no longer waiting on task t2 to complete but that doesn't mean task t2 will be stopped. It will run to its complete execution. Also, main thread is now free to continue with its further execution.

This brings me to another conclusion that you might be missing Console.ReadKey() in Main method. That's why your console application close once exception gets thrown (main thread is not waiting anymore on task t2). Hence, you never see Finish to be printed on Console (assuming you run loop with ctr < Int32.MaxValue).

If you want task t2 to be stopped you can call token.ThrowIfCancellationRequested(); which will cause task t2 to be stopped if IsCancellationRequested is set to true for token.

for (int ctr = 0; ctr < Int32.MaxValue; ctr++)
{
    token.ThrowIfCancellationRequested();
    if (ctr % 100000000 == 0)
       Debug.WriteLine(ctr.ToString());
}
Rohit Vats
  • 79,502
  • 12
  • 161
  • 185
  • No I tested at ctr < Int32.MaxValue; I posted the code. See the sleep I put in. If I change the sleep it it processes different amounts. And CPU got to zero as soon as I get press any key to continue message (not after I hit enter) – paparazzo Mar 16 '14 at 21:06
  • See post. I put in a sleep to verify it ended on press any key. I still contend that sample in the link from Microsoft is misleading at best. – paparazzo Mar 16 '14 at 21:47
  • Like I mentioned in the answer you was missing `Console.ReadKey()` so your console app closes as soon as Main finishes its execution. I agree with you the MSDN sample should have been more explanatory though. – Rohit Vats Mar 17 '14 at 06:49