Tags

, , ,

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:

  1. Thread
  2. ThreadPool
  3. 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.

threadpool

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.

Advertisements