Threads in C# - Behind the Scene — ParameterizedThreadStart

Ghadeer Kenawi
7 min readJan 13, 2019

--

This article explains how the CLR works with threads behind the scene, and how to pass a void parameterized method to a thread instance to execute it.

There are two types of methods that can be passed as parameters to threads: A void method with no parameters and the second type is a void method which takes one parameter of the Object data type.

Behind the scene :

While all that we need to do to pass a method to a thread instance is to put the method name in the brackets ,Thread t = new Thread(MethodName); , internally, the CLR breaks down this one line of code into two lines ;It creates the ThreadStart (for void nonparameterized methods) or ParameterizedThreadStart(for void methods with one Object type parameter) delegate instances and assign it a matching signature method (as a delegate instance must point to a method which signature has to match the delegate signature).

Let’s explain the topics above with a console application example :

We will create a console application which prints a product’s unit price to the user, then prompt the user to enter how many he wants to buy, and finally prints out the total price. The application will have two methods: PrintProductUnitPrice(); void method with no parameters, prints out a message to the user including the unit price of the product (for simplicity will use a fixed price amount). GetTotalPrice(Object numberOrdered); void method with one Object type parameter which is the count number of products the user wants to order. In a real-life application, these two methods would be part of a huge application where they work as helper methods or so.

In Visual Studio ,create a C# console application , call it ProductPriceInfo .

After creating the application, create the first method PrintProductUnitPrice():

PrintProductUnitPrice()

Before creating the second method, let’s create a thread to execute the first method :

Thread t1 = new Thread(PrintProductUnitPrice);

That was a short way, we can also do a long way which is the same way CLR does behind the scene :

Inspecting the Thread method using visual studio intellisense, we can see in the figure below that the Thread method takes a delegate parameter of type ThreadStart

Now if we inspect the ThreadStart delegate, as seen in the figures below,

Figure a
Figure b

Using visual studio intellisense ; In Figures a and b, we can see that the ThreadStart is a delegate that is void with no parameters. To create an instance of the ThreadStart delegate, a method with the same delegate’s signature has to be passed to the ThreadStart instance. The PrintProductUnitPrice() method is void with no parameters, so we can pass it to the ThreadStart delegate

ThreadStart ts = new ThreadStart(PrintProductUnitPrice)

Then create a thread instance, t1, and pass the above ThreadStart instance, ts, as an argument:

Thread t1 = new Thread(ts);

And so as we discussed earlier in this article, the above two lines can be replaced with :

Thread t1 = new Thread(PrintProductUnitPrice);

To start executing the thread t1, just call the Start method

t1.Start(); This will start the thread

Now let's create the second method, GetTotalPrice(Object numberOrdered); notice that this method calculates the total price based on the number of orders, so looks like this method should have an int as a parameter instead of Object , but for this article and because we want to assign this method to a separated thread to execute it; looking for a thread instance which takes a void parameterized method as a parameter ,you will find out that the only type of methods we can pass a parameter to the Thread method is one that is void and takes only one parameter and it has to be of type Object.

Let's create a Thread, t2 and pass the GetTotalPrice method as a parameter:

Thread t2 = new Thread(GetTotalPrice);

Again, That was a short way, we can also do a long way which is the same way CLR does it behind the scene :

Inspecting the Thread method again using visual studio intellisense, we can see in the figure below that the Thread method is overloaded with 4 types of methods, one of them takes a parameter of type ParameterizedThreadStart

So for the long way, we have to create a ParameterizedThreadStart instance and pass the GetTotalPrice method as an argument

From the figure above we can see that the ParameterizedThreadStart delegate takes a void method with an Object type parameter. Create the ParameterizedThreadStart instance:

ParameterizedThreadStart pts = new ParameterizedThreadStart(GetTotalPrice) ;

Now create a Thread and pass the above instance as an argument:

Thread t2 = new Thread(pts);

And so as we discussed earlier in this article, the above two lines can be replaced with :

Thread t1 = new Thread(GetTotalPrice); Here the control is able to recognize that GetTotalPrice is a void parameterized method with one parameter of type Object.

To start executing the thread t1, call the Start method but because we passed a parameterized method, we have to pass an argument of type Object to the Start method :

Let's say the user wants to order 30 products :

t2.Start(30); The 30 here is an argument of type Object, not Int.

We still have not created the GetTotalPrice method; the reason of that and why I put it till the end, is there will be some tweaks that we need to do to be able to process the Object argument and make it suitable to be used in calculations. So, looks like we have to find a way to convert it from Object to Int in order to calculate the total price. The GetTotalPrice method takes an Object as an argument, convert it to Int, calculates the total and prints it out.

GetTotalPrice()

GetTotalPrice()

In the code above, I used the int.TryParse method to convert the Object parameter to Int. I chose the TryParse method because it checks if the conversion was successful then assigns the converted variable to the out parameter and return true or false. The TryParse method doesn’t throw an exception if the conversion failed, and that makes it better for handling errors if it returns false. If the conversion is successful, the TryParse will return true and print out the results, if the conversion failed, the controller will move to the else block and prints out an Error message. If we passe other data types than numbers to the Start method, like for example if we pass a string, t2.Start(“Ghadeer”), the string is still of the data type Object, so we will not get a compiling error, that is why we use TryParse with error handling in the method body.

Before I post the whole code and discuss the results, one more thing needs to be addressed; since we have two threads, t1 and t2 running by the main thread, and as we learned from my Part1 Article on Threads, the main thread starts running one of the two threads, but before the thread is done, the controller jumps to execute the second thread and again jumps to resume executing the first one. In order to avoid this, we can use the Join method on thread t1(since it is the thread starts first) to ask the main thread to wait till t1 is done and then moves to call the second thread, t2.

Main method (main thread)

RESULTS:

Now let’s comment out line 21, and uncomment line 24 (passing a string), running the application : RESULTS:

From the results above, we can see that when we passed a string to the start method in line 24, the application sent the error message we assigned in the TryParse telling the user that there is an error happened.No exception was thrown.

Other articles by Ghadeer Kenawi :

Threads in C# — Part1: Single-threaded vs Multi-threaded applications

Reflection in C# — How to load assembly and retrieve its metadata using Reflection

C#-Fluent Interfaces for Unit Testing

Solving Technical Mysteries: A Step-by-Step Guide to Being a Successful Software Support Engineer

Resources:

Threads and threading | Microsoft Docs

ParameterizedThreadStart Delegate | Microsoft Docs

Int32.TryParse Method (System) | Microsoft Docs

--

--

Ghadeer Kenawi

Professional Software Support Engineer at : IBMWatson ,UnitedHealthGroup ,Generac , IMC & more . LinkedIn : https://www.linkedin.com/in/ghadeer-kenawi-b480b311/