Thread.Sleep (.NET) vs Sleep Function (WinAPI)

Thread.Sleep is a sign of a poorly designed program.

by Peter Ritchie.

Thread.Sleep has it’s use: simulating lengthy operations while testing/debugging on an MTA thread.  In .NET there’s no other reason to use it.

Thread.Sleep(n) means block the current thread for at least the number of timeslices (or thread quantums) that can occur within n milliseconds.  The length of a timeslice is different on different versions/types of Windows and different processors and generally ranges from 15 to 30 milliseconds.  This means the thread is almost guaranteed to block for more than n milliseconds.  The likelihood that your thread will re-awaken exactly after n milliseconds is about as impossible as impossible can be.  So, Thread.Sleep is pointless for timing. 

Threads are a limited resource, they take approximately 200,000 cycles to create and about 100,000 cycles to destroy.  By default they reserve 1 megabyte of virtual memory for its stack and use 2,000-8,000 cycles for each context switch.  This makes any waiting thread a *huge* waste.

If the current thread is a foreground thread, Thread.Sleep(n) also means your application cannot exit for >n milliseconds.  After all foreground threads have completed, the CLR will allow an application to terminate.  If the current thread is a background thread, and the application exits the thread will simply never be awoken (not good if you need your objects to be disposed or finalized).

STA threads have an implicit requirement to not make blocking calls.  STA threads use a message queue (of finite size) to communicate with the outside world; that communication occurs whether you explicitly use it or not.  When you block your STA thread with Thread.Sleep you’re blocking that communication and run the risk of the queue overflowing.  A WinForm thread must be STA, so it has the same implicit requirement to not make blocking calls simply to support being an apartment.  A GUI should never make calls that can affect the responsiveness of the user interface, for obvious reasons.

There is one non-breaking use for Thread.Sleep: Thread.Sleep(0).  This tells the system you want to forfeit the rest of the thread’s timeslice and let another, waiting, thread run.  If there are no other threads waiting to run you still relinquish your timeslice.  If There are other threads waiting to run, you won’t be sure when you get control back; if the waiting thread has a higher priority, you may never get control back.  Thread.Sleep(0) effectively tells the OS that you’re better at scheduling processes than it is and you’ll likely affect the way it can schedule threads and processes and affect the responsiveness of the entire system if you’re using Sleep(0) a lot.

As future versions of the CLR and CLR hosts are implemented they will eventually implement threading without a direct dependence on unmanaged threads (e.g. managed entirely by the CLR using fibers.)  Which means Thread.Sleep will have to be reimplemented to do something different (e.g. if Sleep(0) means relinquish the current thread’s timeslice to the OS and there is no current OS thread then Sleep(0) means nothing.  If Thread.Sleep is not re-implemented Thread.Thread(0) means block all fibers, or managed threads, associated with the current OS thread.  You never want to cause other threads to suspend, especially arbitrarily).  So, System.Thread is not future-friendly.

Thread.Sleep has been used for many things it shouldn’t be used for.  Here’s a list of the common mistakes:

The thread needs to wait for another thread to complete
In this case no value, other than infinite, passed to Thread.Sleep will be correct.  You simply don’t know when the other thread will complete using this method.  If the thread completed after Sleep returned you’ll likely have synchronization problems.  If the other thread completed before Sleep returned the thread was needlessly blocked for an amount of time rendering the benefits of multithreading limited or moot.  In the control circumstances where you’ve tested this it may seem like it always works; it just takes a busy program to cause it to faile: a defrag program, a sudden influx of network traffic, a network hiccup, etc.

The thread needs perform logic every n milliseconds
As noted earlier, Sleep means relinquish control.  When your thread gets control again isn’t up to the thread; so it can’t be used for periodic logic.

We don’t know why Thread.Sleep is required; but if we take it out the application stops working
This is flawed logic because the application still doesn’t work with Thread.Sleep.  This is really just spackling over the problem on that particular computer.  The original problem is likely a timing/synchronization issue, ignoring it by hiding it with Thread.Sleep is only going to delay the problem and make it occur in random, hard to reproduce ways.


What about Sleep function documented in Windows API?

Using the Sleep function by Windows API and Thread.Sleep is 2 entirely different thing.  While I was creating a program which require the thread to wait until a certain event was called, having no Thread.Sleep was a big problem.  CPU usage rose up to 50%.  Clearly something had to be done.  Using Thread.Sleep generally prevents catch of exception which I need to handle in ThreadAbortException.  With Peter Ritchie’s article, it made clear that I will never going to try and attempt to use Thread.Sleep again without a better document on it.  What struck me was the Sleep I always use in C programming language.  However, mixing .NET with WinAPI certainly is a problem arise already but nonetheless usable in certain cases.  Simply important the function from kernel library and replaced it with Thread.Sleep solved my problem immediately.  ThreadAbortException was catched by the program with WinAPI’s Sleep but not with .NET’s Thread.Sleep.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: