Click here to Skip to main content
15,867,686 members
Articles / General Programming / Threads
Tip/Trick

Use CancellationToken - not Thread.Sleep

Rate me:
Please Sign up or sign in to vote.
4.95/5 (17 votes)
13 May 2020CPOL2 min read 24K   218   10   12
Thread.Sleep cannot be cancelled. To inject a pause, consider using the Cancellation Token instead.
It is quite common to discover a need to inject a pause in the code execution, a remote target or file may not be ready at first glance, so you back off for a period and try again, repeating until the target is ready or the process is cancelled. In such a case, it is tempting to put in a Thread.Sleep(n) - but this will not respond to a cancellation trigger, so why not use the CancellationToken instead?

Introduction

Not so long ago, I was asked to take a look at a service which was intermittently generating exceptions on stop and shutdown operations.

The service itself was a simple device management component, using a task to connect to a remote device when it had booted up, doing some updates and that was sort of it. But the target device was not always ready when the connection attempt was made, so a while loop was used with a pause injected in the form of a call to Thread.Sleep(20000).

The idea of pausing a task for 20 seconds odd, before trying again makes perfect sense - but the 20 second thread block would intermittently, dependent on timing, cause the service manager to forcibly kill off the service due to shutdown or stop timeouts being triggered, and resulting exceptions would fill the logs.

Thread.Sleep(n) cannot be cancelled - instead, consider using the CancellationToken.WaitHandle.WaitOne(n).

Using the Code

The code in this tip is a little sample of how Thread.Sleep and CancellationToken.WaitHandle.WaitOne behave in tasks, that you can experiment with.

The original while loop implemented in the service looked something like this:

C#
while (!cancellationToken.IsCancellationRequested)
{
    // Processing
    if(connectionReady)
    {
        // Do its business
        break;
    }
    // Pause for 20 seconds before trying again
    Thread.Sleep(20000);
}

In this implementation, the thread will be blocked for 20 seconds - irrespective of any cancellation triggers. This was the underlying problem - dependent on the timing of the stop operation in relation to the Thread.Sleep call, the service manager would timeout the stop operation and forcibly kill the management service.

Pausing code execution, but being cancellation request aware is simple enough:

C#
while (!cancellationToken.IsCancellationRequested) 
{ 
    // Processing
    if(connectionReady)
    {
        // Do its business
        break;
    }
    // Pause for 20 seconds before trying again - now with cancellation support
    var cancellationTriggered = cancellationToken.WaitHandle.WaitOne(20000);
}

This one line change achieves the same 20 second pause that the original implementation did, and it is also task cancellation aware.

Points of Interest

If you find Thread.Sleep(n) used around the place, consider switching to using the CancellationToken.WaitHandle.WaitOne(n) method instead. It will help in keeping the exceptions log down to size.

History

  • 13th May, 2020: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Emile van Gerwen18-May-20 21:14
Emile van Gerwen18-May-20 21:14 
QuestionThere is a better way of doing this... Pin
Member 1200388014-May-20 0:55
professionalMember 1200388014-May-20 0:55 
AnswerRe: There is a better way of doing this... Pin
Thomas Roll14-May-20 3:06
Thomas Roll14-May-20 3:06 
GeneralRe: There is a better way of doing this... Pin
Member 1200388014-May-20 5:58
professionalMember 1200388014-May-20 5:58 
QuestionI'm with mathew Pin
Sacha Barber13-May-20 6:34
Sacha Barber13-May-20 6:34 
Use Task.Delay()

If you want same behavior just create extension methods and catch task cancelled

We use a nice one for cancelling task after a period

This is for http client but you get the idea

Better timeout handling with HttpClient - Thomas Levesque's .NET Blog
AnswerRe: I'm with mathew Pin
Thomas Roll13-May-20 7:13
Thomas Roll13-May-20 7:13 
GeneralRe: I'm with mathew Pin
Matthew Dennis13-May-20 8:36
sysadminMatthew Dennis13-May-20 8:36 
GeneralRe: I'm with mathew Pin
George Swan13-May-20 9:07
mveGeorge Swan13-May-20 9:07 
GeneralRe: I'm with mathew Pin
Matthew Dennis13-May-20 14:32
sysadminMatthew Dennis13-May-20 14:32 
QuestionWhy not ... Pin
Matthew Dennis13-May-20 5:00
sysadminMatthew Dennis13-May-20 5:00 
AnswerRe: Why not ... Pin
Thomas Roll13-May-20 5:32
Thomas Roll13-May-20 5:32 
GeneralRe: Why not ... Pin
Matthew Dennis13-May-20 5:46
sysadminMatthew Dennis13-May-20 5:46 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.