When to Use Threads:
A common application for multithreading is performing time-Consuming tasks in the background. The main thread keeps running, while the worker thread does its background job.
With Windows Forms applications, if the main thread is tied up performing a lengthy operation, keyboard and mouse messages cannot be processed, and the application becomes unresponsive.
For this reason, it’s worth running time-consuming tasks on worker threads even if the main thread has the user stuck on a “Processing… please wait” modal dialog in cases where the program can’t proceed until a particular task is complete.
The modal dialog approach also allows for implementing a "Cancel" button, since the modal form will continue to receive events while the actual task is performed on the worker thread.The BackgroundWorker class assists in just this pattern of use.
In the case of non-UI applications, such as a Windows Service, ultithreading makes particular sense when a task is potentially time-consuming because it’s awaiting a response from another computer (such as an application server, database server, or client).
Having a worker thread perform the task means the instigating thread is immediately free to do other things.
Another use for multithreading is in methods that perform intensive calculations. Such methods can execute faster on a multi-processor computer if the workload is divided amongst multiple Threads. (One can test for the number of processors via the Environment.ProcessorCount property).
A C# application can become multi-threaded in two ways: either by explicitly creating and running additional threads, or using a feature of the .NET framework that implicitly creates threads – such as BackgroundWorker, threads pooling, a threading timer, a Remoting server, or a Web Services or ASP.NET application. In these latter cases, one has no choice but to embrace multithreading. A single-threaded web server would not be cool – even if such a thing were possible!
Fortunately, with stateless application servers, multithreading are usually fairly simple; one's only concern perhaps being in providing appropriate locking mechanisms around data Cached in static variables.
When Not to Use Threads
Multithreading also comes with disadvantages. The biggest is that it can lead to vastly more complex programs.
Having multiple threads does not in itself create complexity; it's the interaction between the threads that creates complexity. This applies whether or not the interaction is intentional, and can result long development cycles, as well as an ongoing susceptibility to intermittent and non-reproducible bugs.
For this reason, it pays to keep such interaction in a multi-threaded design simple – or not use multithreading at all – unless you have a peculiar penchant for re-writing and debugging! Multithreading also comes with a resource and CPU cost in allocating and switching threads if used excessively. In particular, when heavy disk I/O is involved, it can be faster to have just one or two workers thread performing tasks in sequence, rather than having a multitude of threads each executing a task at the same time. Later we describe how to implement a Producer/Consumer queue, which provides just this functionality.