36

I have a task and I expect it to take under a second to run but if it takes longer than a few seconds I want to cancel the task.

For example:

Task t = new Task(() =>
        {
            while (true)
            {
                Thread.Sleep(500);
            }
        });
t.Start();
t.Wait(3000);

Notice that after 3000 milliseconds the wait expires. Was the task canceled when the timeout expired or is the task still running?

cuongle
  • 74,024
  • 28
  • 151
  • 206
Paul Mendoza
  • 5,709
  • 12
  • 53
  • 82

5 Answers5

54

Task.Wait() waits up to specified period for task completion and returns whether the task completed in the specified amount of time (or earlier) or not. The task itself is not modified and does not rely on waiting.

Read nice series: Parallelism in .NET, Parallelism in .NET – Part 10, Cancellation in PLINQ and the Parallel class by Reed Copsey

And: .NET 4 Cancellation Framework / Parallel Programming: Task Cancellation

Check following code:

var cts = new CancellationTokenSource();

var newTask = Task.Factory.StartNew(state =>
                           {
                              var token = (CancellationToken)state;
                              while (!token.IsCancellationRequested)
                              {
                              }
                              token.ThrowIfCancellationRequested();
                           }, cts.Token, cts.Token);


if (!newTask.Wait(3000, cts.Token)) cts.Cancel();
Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Nick Martyshchenko
  • 4,231
  • 2
  • 20
  • 24
  • I noticed that the `cts.Token` is also passed to the `Wait()` method. I found a note in Microsoft's documentation: _"Passing the cancellationToken object to this method simply allows the wait to be canceled based on some condition."_ https://msdn.microsoft.com/en-us/library/dd321457%28v=vs.110%29.aspx – Michael R Apr 30 '15 at 22:24
  • Last row can look prettier, like this: `cts.CancelAfter(3000); newTask.Wait(cts.Token);` – Andrii Viazovskyi Dec 04 '18 at 08:43
  • CancelAfter is not available in .Net 4 – AC25 Jun 09 '23 at 14:39
17

If you want to cancel a Task, you should pass in a CancellationToken when you create the task. That will allow you to cancel the Task from the outside. You could tie cancellation to a timer if you want.

To create a Task with a Cancellation token see this example:

var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;

var t = Task.Factory.StartNew(() => {
    // do some work
    if (token.IsCancellationRequested) {
        // Clean up as needed here ....
    }
    token.ThrowIfCancellationRequested();
}, token);

To cancel the Task call Cancel() on the tokenSource.

Brian Rasmussen
  • 114,645
  • 34
  • 221
  • 317
  • 3
    Doesn't this allow you to only *request* cancellation "from the outside"? Wouldn't it be up to the delegate whether it pays attention to the token-based request and actually throws or just continues working without cancellation? – BitMask777 Nov 20 '14 at 22:06
  • Correct, the delegate decides whether it will act on the request by checking the `IsCancellationRequest` on the property. – Brian Rasmussen Nov 21 '14 at 16:44
7

The task is still running until you explicitly tell it to stop or your loop finishes (which will never happen).

You can check the return value of Wait to see this:

(from http://msdn.microsoft.com/en-us/library/dd235606.aspx) Return Value

Type: System.Boolean true if the Task completed execution within the allotted time; otherwise, false.

justinmvieira
  • 572
  • 4
  • 13
6

Was the task canceled when the timeout expired or is the task still running?

No and Yes.

The timeout passed to Task.Wait is for the Wait, not the task.

Richard
  • 106,783
  • 21
  • 203
  • 265
  • This is interesting, because the help text here seems to say the opposite: https://msdn.microsoft.com/en-us/library/dd270644(v=vs.110).aspx says, _"Waits for the Task to complete execution within a specified number of milliseconds."_ Not sure if I understand this yet. – Michael R Apr 30 '15 at 18:10
  • @Michael If the time passed to `Wait` expires it will return, even if the task has not completed. In this timeout case the task will continue to execute until it completes (and a later `Wait` would thus return as completed). – Richard May 01 '15 at 07:20
-1

If your task calls any synchronous method that does any kind of I/O or other unspecified action that takes time, then there is no general way to "cancel" it.

Depending on how you try to "cancel" it, one of the following may happen:

  • The operation actually gets canceled and the resource it works on is in a stable state (You were lucky!)
  • The operation actually gets canceled and the resource it works on is in an inconsistent state (potentially causing all sorts of problems later)
  • The operation continues and potentially interferes with whatever your other code is doing (potentially causing all sorts of problems later)
  • The operation fails or causes your process to crash.
  • You don't know what happens, because it is undocumented

There are valid scenarios where you can and probably should cancel a task using one of the generic methods described in the other answers. But if you are here because you want to interrupt a specific synchronous method, better see the documentation of that method to find out if there is a way to interrupt it, if it has a "timeout" parameter, or if there is an interruptible variation of it.

Florian Winter
  • 4,750
  • 1
  • 44
  • 69