The Parallel Extensions library is extraordinarily useful because it allows you to manipulate a higher-level abstraction—the task—rather than working directly with threads. Sometimes, however, you might need to work with code written before the TPL and PLINQ were available (prior to .NET 4.0), or you might have a programming problem not directly addressed by them. To do this, you can leverage the Thread class and related API in System.Threading. System.Threading.Thread represents a point of control in the program, wrapping operating system threads while the namespace provides additional managed APIs to manage those threads.
One common method in Thread is Sleep(); however, in spite of its convenience, it should be avoided. Thread.Sleep() puts the current thread to sleep, essentially telling the operating system not to schedule any time slices to this thread until (at least) the given amount of time has passed. This might sound like a sensible thing to do, but it is a “bad code smell” that indicates the design of the program could probably be better. Putting a thread to sleep is a bad programming practice because the whole point of allocating an expensive resource like a thread is to get work out of that resource. (You wouldn’t pay an employee to sleep; likewise, you shouldn’t pay the price of allocating an expensive thread only to put it to sleep for millions or billions of processor cycles.) That said, there are a couple of valid use cases.
The first use case is to put a thread to sleep with a time delay of zero to indicate to the operating system that “the current thread is politely giving up the rest of its quantum to another thread if there is one that can use it.” The polite thread will then be scheduled normally, without any further delay. The second use case is in test code to simulate a thread that is working on some high-latency operation, but without actually having to burn a processor doing some pointless arithmetic. Other uses in production code should be reviewed carefully to ensure that a better way to obtain the desired effect is not available.
Another type in System.Threading is the ThreadPool, which is designed to limit an excess number of threads that might negatively impact performance. Threads are expensive resources, thread context switching is not free, and running two jobs in simulated parallelism via time slicing can be significantly slower than running them one after the other. And while the thread pool does its job well, it does not provide services to deal with long-running jobs or jobs that need to be synchronized with the main thread or with one another. What we really need to do is build a higher-level abstraction that can use threads and thread pools as an implementation detail. Since TPL provides precisely that abstraction, ThreadPool can essentially be deprecated entirely in favor of the TPL-based APIs.
For more details on other techniques for managing worker threads that were commonly used prior to .NET 4, see the Essential C# 3.0 multithreading chapters at https://IntelliTect.com/EssentialCSharp .
For more information on System.Threading.ThreadPool’s and System.Threading.Thread’s Sleep() methods, see https://IntelliTect.com/legacy-system-threading .