Threading is not child play
Using thread is a big responsibility and risk as well. Poorly designed multithreaded application could bring nightmare. The moment you decided to use thread there are some fundamental architectural question that you must ask and answer your self. Answer of each question will change the way threading will be implemented in your application.
Theses topics are quite wast but there are some early pointers for further study. Theses points are arrange in order of a decision making sequence.
Does Threading really required for four application.
There are several sacenerios in which we use thread in our application but that could have been avoided by using right design. before using thread rethink your application design if the same problem can be solved using mechanism like Application.DoEvent , Timers or Timeout then prefer not to use thread at all.
Does your thread will try to access UI element.
Usually we should avid accessing UI element from another threads, by default CLR does`t allow it unless we turned off unsafe access, but we should use safe practice and must check if Control::InvokeRequired is true then we must call Control.Invoke to safely update control values. Also checkout the MSDN article How to: Make Thread-Safe Calls to Windows Forms Controls
Alternatively use SynchronizationContextTaskScheduler (a derived type of TaskScheduler) that schedule all the tasks on GUI threads. We mostly use this in windows forms and WPF applications.
Forms.Timers are pretty good in performing small burst of tasks on GUI Threads
Background Vs Foreground Thread
This is an important consideration that really affect overall application design.
Foreground threads should be used for long running task or mission critical task that can not be canceled in between application shutdown.
Background threads should be used for long running task that are not mission critical task and can be canceled in between during application shutdown.
Dedicated Thread Vs Thread Pool
Dedicated thread should be used only when application need full control of task running under thread other wise we should always prefer to use Thread Pool because it has less overhead.
Thread Pool Vs Task
In normal situations we must prefer to use thread pool over task, Task should be only used when we need thread poll like mechanism + full control of task under execution such as cooperative cancellation and result collection.
What Asynchronous Programming Pattern (Join mechanism) will be used.
Join or wait for complete design is also major challenge when writing multithreaded application. Each threading requirement has a different join requirement. Some are supported out of the box and some need several design consideration. This topic will be taken in more details in case study section. The fundamentals architectural of wait of complete is listed below.Actual design depends upon type of threading mechanism used.
Pooling(Busy Wait or Spin Wait) : Also called pooling mechanism in which caller thread wait for child thread to complete after a particular interval. I waste CPU cycle but easy to implement
Wait Until Done (Fork and Join):Main thread start one or more thread and then calls Thread.Join() on all the child threads. Easiest to implement but blocks caller thread, be whould never use this on GUI thread otherwise GUI will be non responsive till all the thread returns.
Callback Notification: Task (ThreadProc) is designed in such a way that it invokes a call back handler to pass the notification caller thread. This is quite efficient mechanism but needs a bit of development overhead. This is quite use full in ThreadPool like implementation.
Task Instance List Pooling: This mechanism is mostly used in conjunction with thread pool implementation. Basic idea is to wrap the task in an instance object with a property IsComplete then instance is pushed to a queue. Then queue is handed over to thread pool. Main thread periodically check for completion status of each task in Task instance queue
Task Result Mechanism:This mechanism can be used with Threading.Task object that have built in support.
How the result of computation will be collected.
There are three fundamentals mechanism of collecting task result, Callback function, Task Instance , and Threading.Task.Result object. Each of them has it`s own limitation and advantage. This will be discussed in a separate page.
What cancellation mechanism will be used.
This is also an very important decision, There are two type of cancellation mechanism available,
Cooperative cancellation: Running task is requested via some mechanism to abort what it is doing
Preemptive cancellation: Owner thread takes control of cancellation and do not request running task to stop gracefully , that`s what we called termination.
What will be Deadlock/Infinite wait detection.
I most of the cases owner thread must implement a mechanism to get of deadlock situation, easiest implementation is to assign a time out after which blocked thread can be terminated but what will be timeout values , is a matter of debate and application specific.
Is your methods are thread Safe?
Most of the time when a developer creates a thread he usually thought of synchronization first, but there are several situations where synchronizations not at all required. If your method is not updating any static variable , only using value type or string type for which a copy is created before any processing then no thread synchronization is required.
What synchronization model will be used.
Most of the time developer start thinking of synchronization as early design factor but it should be taken in to consideration only all above question has been answered. There are several synchronization patterns available each with different scope and limitations. Below is the summary of synchronization mechanism and constructs that can be used in different situations.
Kernel Mode Synchronization Construct
Event
Semaphore
Mutex
User Mode Synchronization Construct and patterns
Volatile read write: Perform Atomic read or write at a time.
Interlocked methods: perform atomic read and write and operations
C# Lock keyword
Hybrid Construct
The ManualResetEventSlim class.
SemaphoreSlim class.
The Monitor Class and Sync Blocks
The ReaderWriterLockSlim Class
The OneManyLock Class
The CountdownEvent Class
The Barrier Class
This is quite wast area of study and I will publish a dedicated page for this but below I will be consolidating when to use what.
What Locking pattern will be used?
Whatever Synchronization construct you use there are certain locking pattern that have been proven over the time are listed below.
What Threading Model Application is Using?
TBD
Threading and Synchronization best practices.
My recommendation always is to avoid writing code that blocks any threads in other words try to avoid using any kind of synchronization at all. At the end of day it degrade application performance.
If locking (synchronization)is necessary first try to use the VolatileRead, VolatileWrite, and Interlocked methods because they are fast and they also never block a thread.
Whenever possible always avoid to use dedicated thread and use solutions like thread pool or task scheduler.
Use the kernel object constructs only if you want to synchronize threads that are running in different AppDomains or processes.
To atomically manipulate state via a set of operations, use the Monitor class (alternatively lock keyword) with a private field.
Use a reader-writer lock instead of Monitor if multiple threads reads the data but only one modified it, which improves overall performance and minimizes the chance of blocking threads.
Avoid using recursive locks (especially recursive reader-writer locks) because they hurt performance.
If recursive lock is to be used use Monitor that is recursive and its performance is very good because it is a hybrid construct and written in native code.
Avoid releasing a lock in a finally block because entering and leaving exception-handling blocks incurs a performance hit,
Any lock must always be used with timeout and your code should not hold the lock for a long time because this increases the likelihood of threads blocking.