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

Published on Thursday, April 26, 2007

Source

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

Thread.Sleep has it'sits 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.i.e. 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.

comments powered by Disqus