How to cancel the task, but wait until the end?

I have a task with a thread that performs some operation in a loop:

static void TaskAction(CancellationToken ct) { while (SomeCondition()) { DoSomeSeriousJob(); ct.ThrowIfCancellationRequested(); } } static void DoSomeSeriousJob() { Console.WriteLine("Serious job started"); Thread.Sleep(5000); Console.WriteLine("Serious job done"); } 

I run it and then cancel after a while:

  var cts = new CancellationTokenSource(); var task = Task.Factory.StartNew(() => TaskAction(cts.Token), cts.Token); Thread.Sleep(1000); cts.Cancel(); 

This operation must be performed correctly, I do not want to interrupt it. But I want to send a request to cancel my task and wait until it finishes correctly (by this I mean that it reaches some point in the code). I tried the following approaches:

1. Wait (CancellationToken ct)

 try { task.Wait(cts.Token); } catch (OperationCanceledException) { Console.WriteLine("Task cancelled"); } // Must be joined here. 

In this case, the program returns immediately from Wait() . The task continues to run until ThrowIfCancellationRequested() , but if the main thread exits, the task also aborts.

2. Wait ()

 try { task.Wait(); } catch (OperationCanceledException) { Console.WriteLine("Task cancelled"); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } 

Here, the main thread is waiting for completion, but at the end, an AggregateException inserted with InnerException = TaskCancelledException (not OperationCancelledException ).

3. Check IsCancellationRequested () and no exceptions

 static void TaskAction(CancellationToken ct) { while (SomeCondition()) { DoSomeSeriousJob(); if (ct.IsCancellationRequested) break; } } // ... task.Wait(); 

In this case, no exceptions occur, but the task receives the status RanToCompletion at the end. This is not different from the correct completion when SomeCodition() starts to return false.

All these problems have easy workarounds, but I wonder if maybe I'm missing something. Can anyone advise me on a better solution?

+5
source share
2 answers

If you want to wait for the task to complete (or to cancel it) synchronously, you can try the following:

 cts.Cancel(); Task.Run(async () => { try { await task; } catch (OperationCanceledException ex) { // ... } ).Wait(); 

So you can directly catch an OperationCanceledException instead of catching an AggregateException.

Edit:

Wait (CanecllationToken)

This approach will not work for this purpose. MSDN :

Waits for a task to complete. The wait terminates if the cancel token is canceled before the task completes.

Wait()

You can use this approach, but as you can see, you should expect that the AggregateException is not an OperationCanceledException. It is also listed in the method docs .

The AggregateException.InnerExceptions collection contains a TaskCanceledException object.

So, in this approach, to make sure the operation is canceled, you can check if the internal patch contains a TaskCanceledException exception or not.

Check IsCancellationRequested () with no exceptions

Thus, it is obvious that the exception is not thrown, and you cannot find out if the operation is canceled or not.

If you do not want to wait synchronously, everything works as expected:

 cts.Cancel(); try { await task; } catch (OperationCanceledException ex) { // ... } 
+5
source

Try the following:

 try { task.GetAwaiter().GetResult(); } catch (OperationCanceledException) { Console.WriteLine("Task cancelled"); } 

You will get an OperationCanceledException and it will not be wrapped with an AggregateException .

0
source

All Articles