In this post I am going to talk about Threads in C# and different methods to interact with them. Threads are lower level of abstraction than tasks. A task object represents an item of work to be performed where as thread object represents a process running within an operating system. Threads and Tasks

Creation of thread is similar to creating a task but there are some key differences

     1.Threads are created as foreground tasks (although they can be set to run as background) which means application cannot exit till the foreground tasks are completed, where as tasks are created as background tasks which means tasks can be terminate before they complete if all the foreground threads in an application complete.

     2.Threads have a priority property that can be changed during the lifetime of the thread. It is not possible to change the priority of the task.

   3.A thread cannot deliver a result to another thread. They must communicate through shared variables which causes synchronization issues.

      4.There is no continuation in threads, they provide a method called join, which allows one thread to pause while other completes

     5.It is not possible to aggregate exceptions over a number of threads like tasks. An exception thrown inside a thread must be caught and dealt by the code in that thread.

          Create a thread

A thread is created by creating an instance of Thread class and by passing in the method to the constructor. Once the thread has been created start method on the thread can be invoked to run it. Below code shows it in use.

Threads and lambda expressions

Threads can be created by lambda expression by passing into the expression the action of the thread. Below code shows an example of creating a thread by lambda expression. The output of this program will be Press a key to end following with Hello from the thread as the main thread starts the new thread but by the time new thread starts running main thread has reached the writeline statement.

Passing data into thread

A program can pass data into thread when it is created by using the ParameterizedThreadStart delegate. This specifies the thread method as one that accepts single object parameter. The object to be passed into the thread is supplied to the start method.

This can also be achieved by using lambda expression. The input parameter of lambda would be the object passed to the thread method. The data passed to thread method is always an object reference so there is no way to be sure at compile time the thread initialization is performed with a particular type of data.

Abort a thread

Thread object exposes an abort method which can be used to abort the thread execution. This would stop the execution of thread instantly. Below code shows this in use. Using Abort method can lead to program being in an ambiguous state which means files and resources of the program may be left open. If you try running this program you may encounter Platform not supported exception.

The better way of aborting the thread is by using a shared flag variable. Below example shows variable tickRunning being used to terminate the loop and thereby aborting the thread.

Thread Synchronization using Joins

The join method allows threads to synchronized. When a thread calls join method the caller of join is held until other thread completes. Below is an example of it where main thread waits for threadToWaitFor

Thread data storage and ThreadLocal

Earlier we saw an example of using the same flag variable shared between threads. If you want the threads to have their own local variables you can specify by using ThreadStatic attribute to specify that the given variable needs to be created for each thread.

To initialize local data for each thread ThreadLocal class can be used. When an instance of ThreadLocal is created it is given a delegate to the code that will initialize the attributes of the threads. Below shows example of it in use.

The RandomGenerator member of the class returns a random number generator that is to be used by the thread to produce random behaviors. To achieve same random behavior for each thread it is called using the same seed.

Thread execution context

A thread instance exposes a range of context information, where some of them are read and others are read and set. This information includes name of the thread, whether it is background or foreground. Below example shows some of the properties of thread.

Thread pools

Threads like everything else are managed as objects in C#. If a program uses large number of threads all these threads need to be created and then destroyed when thread completes. A thread pool consists of reusable thread objects. Rather than creating new thread instance, an application can request it to be run on a thread from thread pool. After completion, the thread is returned to the pool which can be used by another process. ThreadPool provides a method QueueUserWorkItem which allocates the thread to run the supplied item of work.

ThreadPool restricts the number of active threads and maintains the queue of threads waiting to execute. You should not be using ThreadPool if you have threads that many be idle for very long time. ThreadPool runs all threads in background and they cannot have foreground priority. Local state variable should not be used as they are not cleared when a ThreadPool is reused

GitHub Link: Threads
Finally, on my fitness part I hit my gym today, but I need to be more productive there. #5DayOf100DaysOfCode
Last modified: March 17, 2019

Author

Comments

Write a Reply or Comment

Your email address will not be published.