Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cancel Task but wait until it finishes?

I have threaded task wich performs some operation in 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 start it and then cancel after some period of time:

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

This operation must be finished correctly, I don't want to interrupt it. But I want to send a cancellation request to my task and wait until it finishes correctly (by which I mean it gets to some point in code). I tryed following approaches:

1. Wait(CancellationToken ct)

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

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

2. Wait()

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

Here main thread waits for completion but at the end AggregateException is risen 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 are risen but the task gets status RanToCompletion in the end. This is not distiguishable from correct completion when SomeCodition() starts to return false.

All these problem have easy workarounds but I wonder, may be I'm missing something? Could anybody advise me better solution?

like image 755
greatvovan Avatar asked Apr 28 '15 15:04

greatvovan


People also ask

How does task Wait work?

Wait is a synchronization method that causes the calling thread to wait until the current task has completed. If the current task has not started execution, the Wait method attempts to remove the task from the scheduler and execute it inline on the current thread.

How do you cancel asynchronous tasks?

You can cancel an asynchronous operation after a period of time by using the CancellationTokenSource. CancelAfter method if you don't want to wait for the operation to finish.

What is task Cancelled exception?

TaskCanceledException(String, Exception, CancellationToken) Initializes a new instance of the TaskCanceledException class with a specified error message, a reference to the inner exception that is the cause of this exception, and the CancellationToken that triggered the cancellation. TaskCanceledException(Task)

What is the way to create a task that can be Cancelled after it starts?

Create and start a cancelable task. Pass a cancellation token to your user delegate and optionally to the task instance. Notice and respond to the cancellation request in your user delegate. Optionally notice on the calling thread that the task was canceled.


1 Answers

If you want to wait for the task to complete (or gets cancelled) synchronously, you can try this:

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

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

Edit:

Wait(CanecllationToken)

This approach won't work for that purpose. MSDN statement:

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

Wait()

You can use this approach but as you can see, you should expect an AggregateException not OperationCanceledException. It is also specified in documents of the method.

The AggregateException.InnerExceptions collection contains a TaskCanceledException object.

So in this approach, in order to make sure operation is cancelled, you can check if inner expection contains a TaskCanceledException or not.

Check IsCancellationRequested() and no exceptions

In this way, this is obvious that no exception is thrown and you can't find out if the operation is cancelled or not.

If you don't want to wait synchronously, everything works as expected:

cts.Cancel();
try {
    await task;
}
catch (OperationCanceledException ex) {
   // ...
}
like image 118
Mehrzad Chehraz Avatar answered Sep 19 '22 18:09

Mehrzad Chehraz