Real world software uses concurrency. As you know concurrency has several advantages, main thing is it simulates several things in one time concept. .NET gives three ways for the developers to achieve concurrency while we write code. They are:
Thread
ThreadPool
Task
Thread
These are very low level elements of concurrency. They have their own stack and OS level resources.
Dont confuse .NET Threads with OS threads actually Threads in .NET are CLR managed ones. We should technically call these threads as “CLR Threads”. Sometimes we call them as ‘Virtual Threads” while calling OS/kernal threads as “Physical Threads”.
When it comes to relationship between physical (kernal) and virtual (managed) threads, there is a one to many relationship between them. One managed thread can use one or more OS (kernal) threads. Its up to the Managed Scheduler to decide. So a managed thread could use a Windows Fiber to achieve its execution instead of Windows Threads.
Managed threads provides highest degree of control to developers where we can Suspend()
, Abort()
and Resume()
threads even these are not a good practices. But they are costly, since they consume an unpredictable amount of memory in stack and they add additional processing overheads since context switch between threads.
In C#, you can do
using System; using System.Threading; class Program { static int _progress = 0; static void Main() { Thread uiThread = new Thread(new ThreadStart(UpdateProgressBar)); Thread dataThread = new Thread(new ThreadStart(MoveData)); uiThread.Start(); dataThread.Start(); uiThread.Join(); dataThread.Join(); } static void UpdateProgressBar() { _progress++; } static void MoveData() { Thread.Sleep(1000); Console.WriteLine("Moving data"); } }
And even you can uiThread.Abort()
. Currently there is no alternative for Thread
in .NET with the highest degree of control. There is no maximum number of Threads in .NET, you can use as maximum as your resources allows you to do.
In Visual Studio you can debug threads in the application execution. When your application is running, go to Debug -> Windows -> Threads. In this windows you can search for an specific thread, group and short threads and freeze or thew threads. You can learn more about Threads Window here.
ThreadPool
ThreadPool is a collection of fixed numbered threads during the initialization of the application and then reuse them for new tasks then required. Now every executable has a fixed number of threads in them depending in the available free memory.
Since ThreadPool
uses a fixed number of threads every-time they won’t let used memory to grow dramatically like Threads do, there are safer than Threads
. ThreadPools uses a queue that have Tasks and pick Tasks from the queue, execute them and once completed put those tasks to another queue that contains the completed tasks.
We can use System.Threading.ThreadPool
to create thread pools in .NET and no need to specify how many threads should be there is the pool. ThreadPool
manages itself.
ThreadPool
class does the following activities
- Execute tasks
- post work items
- Process asynchronous I/O
- Wait on behalf of other threads
- Process timers
We can implement ThreadPool as below
using System; using System.Threading; class Program { public static void Main() { // Queue the task. ThreadPool.QueueUserWorkItem(ThreadPoolMethod); Console.WriteLine("Main thread starts"); Thread.Sleep(1000); Console.WriteLine("Main thread exits."); Console.ReadKey(); } static void ThreadPoolMethod(Object stateInfo) { Console.WriteLine("Hello from the thread pool."); } }
this will give output like
--------------------------- Main thread starts Hello from the thread pool. Main thread exits. ---------------------------
If we comment Thread.Sleep(1000);
line, you can notice, Main thread exits before thread pool method gets executed. Using this we can understand ThreadPool
used background threads they wont keep the program running once all foreground threads get terminated.
You can define the number of threads to be handled in Threadpool via ThreadPool.SetMaxThreads
and ThreadPool.SetMinThreads
but its a best practice not to set them and let CLR handles with the default value. In .NET 2.0 and 3.0 the default value of MaxThread has a direct relation with the CPU cores. In .NET 2.0, it was 50 times of CPU cores and in .NET 3.0 it was 250 times of CPU cores but after .NET 4.0 it depends on several other factors also such as the size of virtual address space. You can always determine this by calling ThreadPool.GetMaxThreads
. Another useful method is ThreadPool.GetAvailableThreads
.
Conclusion is, concurrency in .NET is sexy but you have to be very careful when you handle this, else you will end up in a total disaster. Despite, having some limitations, Threadpool has more advantages over Threads but Tasks are the best practice when we think about concurrency in .NET still they have more limitations yet they are safer than Threadpool or Thread. I will cover them in next post.
Leave a comment