0% found this document useful (0 votes)
17 views20 pages

C# Thread Class Join Method Explained

The document explains the Join method of the Thread class in C#, which allows the main thread to wait for child threads to complete their execution. It provides examples of using the Join method, including its overloaded versions that allow for waiting with or without a timeout. Additionally, it discusses thread synchronization and how to prevent data inconsistency when multiple threads access shared resources simultaneously.

Uploaded by

rupams2024
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
17 views20 pages

C# Thread Class Join Method Explained

The document explains the Join method of the Thread class in C#, which allows the main thread to wait for child threads to complete their execution. It provides examples of using the Join method, including its overloaded versions that allow for waiting with or without a timeout. Additionally, it discusses thread synchronization and how to prevent data inconsistency when multiple threads access shared resources simultaneously.

Uploaded by

rupams2024
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

Join Method of Thread Class in C#.

Let us understand the use of the Join Method of Thread Class in C# with Examples.
For a better understanding, please have a look at the following example. In the below
example we have created three methods and then execute these three methods
using three different threads. The point that you need to remember is the threads
thread1, thread2, and thread3 are called the child threads of the main thread. This is
because these three threads are created by the main thread only. Here, thread1
executes Method1, thread2 executes Method2, and thread3 executes Method3.
Further,

if you notice,

we have delayed the execution of Method1 by 3 seconds, Method2 execution by 2


seconds, and Method3 execution by 5 seconds

using the Thread class static Sleep method which takes the time in milliseconds and
will make the current thread sleep for that millisecond.

Here, we are using the overloaded version of the Thread class constructor which
takes the ThreadStart delegate as a parameter.

using [Link];
using System;
namespace ThreadingDemo
{
class Program
{
static void Main(string[] args)
{
[Link]("Main Thread Started");
//Main Thread creating three child threads
Thread thread1 = new Thread(Method1);
Thread thread2 = new Thread(Method2);
Thread thread3 = new Thread(Method3);
[Link]();
[Link]();
[Link]();
[Link]("Main Thread Ended");
[Link]();
}
static void Method1()
{
[Link]("Method1 - Thread1 Started");
[Link](3000);

1
[Link]("Method1 - Thread 1 Ended");
}
static void Method2()
{
[Link]("Method2 - Thread2 Started");
[Link](2000);
[Link]("Method2 - Thread2 Ended");
}
static void Method3()
{
[Link]("Method3 - Thread3 Started");
[Link](5000);
[Link]("Method3 - Thread3 Ended");
}
}
}
Now, run the application and see the output. The output may vary on your machine
when you run the application.

As you can see from the above output, the Main thread is not waiting for all the child
threads to complete their execution or task.

If you want the Main thread should not be exited until and unless all the child thread
or any of the child threads completes their task then you need to use the Join method
of the Thread class in C#.

Join Method of Thread Class in C#:

The Join Method of the Thread Class in C# blocks the current thread and makes it
wait until the child thread on which the Join method invoked completes its execution.
There are three overloaded versions available for the Join Method in Thread class as
shown below.

2
The following are the definitions of the above three overloaded versions of the Join
Method of Thread class in C# provided by Microsoft:

1. Join(): This method blocks the calling thread until the thread represented by
this instance terminates while continuing to perform
standard COM and SendMessage pumping.

It will throw ThreadStateException if the caller attempted to join a thread that


is in the [Link] state.

2. Join(int millisecondsTimeout): This method blocks the calling thread until


the thread represented by this instance terminates or the specified time
elapses while continuing to perform standard COM and SendMessage
pumping. The parameter millisecondsTimeout specifies the number of
milliseconds to wait for the thread to terminate. It returns true if the thread has
terminated; false if the thread has not terminated after the amount of time
specified by the millisecondsTimeout parameter has elapsed. It will throw
ArgumentOutOfRangeException if the value of millisecondsTimeout is
negative and is not equal to [Link] in milliseconds.
It will throw ThreadStateException if the thread has not been started.

3. Join(TimeSpan timeout): This method blocks the calling thread until the
thread represented by this instance terminates or the specified time elapses
while continuing to perform standard COM and SendMessage pumping. Here,
the parameter timeout specifies a [Link] is set to the amount of
time to wait for the thread to terminate. It returns true if the thread is
terminated; false if the thread has not terminated after the amount of time
specified by the timeout parameter has elapsed.

It throws ArgumentOutOfRangeException if the value of timeout is negative and is


not equal to [Link] in milliseconds, or greater than
[Link] milliseconds.
It throws ThreadStateException if the caller attempted to join a thread that is in the
[Link] state.
We can simplify the above definitions as follows:

Join(): The first version of the Join method which does not take any parameter will
block the calling thread (i.e. the Parent thread) until the thread (child thread)
completes its execution. In this case, the calling thread is going to wait for an
indefinite time until the thread on which the Join Method is invoked is completed.

Join(int millisecondsTimeout): The second version of the Join Method allows us to


specify the time out. It means it will block the calling thread until the child thread

3
terminates or the specified time elapses. This overloaded takes time in milliseconds.
This method returns true if the thread has terminated and returns false if the thread
has not terminated after the amount of time specified by the millisecondsTimeout
parameter has elapsed.

Join(TimeSpan timeout): The third overloaded version of this method is the same
as the second overloaded version. The only difference is that here we need to use
TimeSpan to set the amount of time to wait for the thread to terminate.

Example to Understand the Join Method of Thread Class in C#

For a better understanding of how to use the Thread Class Join Method in C#, please
have a look at the below example. In the below example, we have called the Join
method on all three threads which means it will block the main thread until all the
child threads complete their tasks.

Advertisements
using [Link];
using System;
namespace ThreadingDemo
{
class Program
{
static void Main(string[] args)
{
[Link]("Main Thread Started");
//Main Thread creating three child threads
Thread thread1 = new Thread(Method1);
Thread thread2 = new Thread(Method2);
Thread thread3 = new Thread(Method3);
[Link]();
[Link]();
[Link]();
[Link](); //Block Main Thread until
thread1 completes its execution
[Link](); //Block Main Thread until
thread2 completes its execution
[Link](); //Block Main Thread until
thread3 completes its execution
[Link]("Main Thread Ended");
[Link]();
}
static void Method1()
{

4
[Link]("Method1 - Thread1 Started");
[Link](1000);
[Link]("Method1 - Thread 1 Ended");
}
static void Method2()
{
[Link]("Method2 - Thread2 Started");
[Link](2000);
[Link]("Method2 - Thread2 Ended");
}
static void Method3()
{
[Link]("Method3 - Thread3 Started");
[Link](5000);
[Link]("Method3 - Thread3 Ended");
}
}
}
Now, run the above code and you will see that the Main thread is started first
and will wait until all the child threads complete their execution as shown in
the below output.

Now, for example, if you don’t want the main thread to wait until thread3 completes
its execution. Then you just need to call the Join method on thread1 and thread2 as
shown in the below example.

using [Link];
using System;
namespace ThreadingDemo
{
class Program
{
static void Main(string[] args)
{

5
[Link]("Main Thread Started");
//Main Thread creating three child threads
Thread thread1 = new Thread(Method1);
Thread thread2 = new Thread(Method2);
Thread thread3 = new Thread(Method3);
[Link]();
[Link]();
[Link]();
[Link](); //Block Main Thread until
thread1 completes its execution
[Link](); //Block Main Thread until
thread2 completes its execution
//Now, Main Thread will not wait for thread3 to
complete its execution
[Link]("Main Thread Ended");
[Link]();
}
static void Method1()
{
[Link]("Method1 - Thread1 Started");
[Link](1000);
[Link]("Method1 - Thread 1 Ended");
}
static void Method2()
{
[Link]("Method2 - Thread2 Started");
[Link](2000);
[Link]("Method2 - Thread2 Ended");
}
static void Method3()
{
[Link]("Method3 - Thread3 Started");
[Link](5000);
[Link]("Method3 - Thread3 Ended");
}
}
}
Output:

6
Other Overloaded Versions of Thread Class Join Method in C#:

You need to use the second and third overloaded version of the Thread Class Join
Method in C# when you want the main thread to wait for a specified amount of time.
For example, you want the main thread to wait for 3 seconds for thread2 and thread3
to complete their task. Then you need to use the Join method as shown below in the
below example. Remember the overloaded versions which take time in milliseconds
and TimeSpan returns a boolean value indicating whether the thread completes its
execution or not. Boolean true means method execution completed and false means
method execution not completed.

using [Link];
using System;
namespace ThreadingDemo
{
class Program
{
static void Main(string[] args)
{
[Link]("Main Thread Started");
//Main Thread creating three child threads
Thread thread1 = new Thread(Method1);
Thread thread2 = new Thread(Method2);
Thread thread3 = new Thread(Method3);
[Link]();
[Link]();
[Link]();
//Now, Main Thread will block for 3 seconds and
wait thread2 to complete its execution
if ([Link]([Link](3)))
{
[Link]("Thread 2 Execution Completed
in 3 second");
}
else

7
{
[Link]("Thread 2 Execution Not
Completed in 3 second");
}
//Now, Main Thread will block for 3 seconds and
wait thread3 to complete its execution
if ([Link](3000))
{
[Link]("Thread 3 Execution Completed
in 3 second");
}
else
{
[Link]("Thread 3 Execution Not
Completed in 3 second");
}
[Link]("Main Thread Ended");
[Link]();
}
static void Method1()
{
[Link]("Method1 - Thread1 Started");
[Link](1000);
[Link]("Method1 - Thread 1 Ended");
}
static void Method2()
{
[Link]("Method2 - Thread2 Started");
[Link](2000);
[Link]("Method2 - Thread2 Ended");
}
static void Method3()
{
[Link]("Method3 - Thread3 Started");
[Link](5000);
[Link]("Method3 - Thread3 Ended");
}
}
}
Now, run the application and observe the output. You will see that Method2
execution is completed in 3 seconds while Method3 execution is not completed.
Further notice after the Main method execution, Method3 is going to complete its

8
execution. So, the Main thread is blocked for 3 seconds and after 3 seconds the Main
thread continues its execution.

IsAlive Property of Thread Class in C#:

The IsAlive property gets a value indicating the execution status of the current
thread. It returns true if the thread has been started and has not terminated normally
or aborted; otherwise, false. That means the IsAlive property of the Thread class
returns true if the thread is still executing else returns false. Let us understand this
with an example.

using [Link];
using System;
namespace ThreadingDemo
{
class Program
{
static void Main(string[] args)
{
[Link]("Main Thread Started");
Thread thread1 = new Thread(Method1);
[Link]();
if ([Link])
{
[Link]("Thread1 Method1 is still
Executing");
}
else
{
[Link]("Thread1 Method1 Completed
its work");
}
//Wait Till thread1 to complete its execution

9
[Link]();
if ([Link])
{
[Link]("Thread1 Method1 is still
Executing");
}
else
{
[Link]("Thread1 Method1 Completed
its work");
}
[Link]("Main Thread Ended");
[Link]();
}
static void Method1()
{
[Link]("Method1 - Thread1 Started");
//Making thread to sleep for 2 seconds
[Link]([Link](2));
[Link]("Method1 - Thread 1 Ended");
}
}
}
Output:

What is Thread Synchronization in C#?

Data inconsistency occurs when more than one threads access a shared resource
such as in-memory data (instance or class variables) and external objects such as
files at the same time.

Let us understand this with an example. Consider that we have two threads

Thread1 and Thread2, and both the threads access a shared resource

10
let’s say Resource1 simultaneously. If Thread1 is trying to read data from the shared
resource Resource1 when Thread2 is attempting to write data onto the shared
resource Resource1, then there would be data inconsistency. Hence, in situations
like this thread synchronization comes into the picture.

Synchronization in C# language is a process that allows access to shared resources


smoothly. Synchronization in C# ensures that only one thread is accessing the
shared resource at any given point in time, preventing other threads from doing the
same at the same time.

Thread Synchronization in C# is a mechanism that is used to restrict multiple threads


from accessing a shared resource at the same time.

In simple words, we can also say that thread synchronization can help us to prevent
multiple threads from gaining access to a shared resource simultaneously

. As a result, we can have one and only one thread entering a critical section to
access the shared resource at any given point in time.

How Thread Synchronization is Achieved in C#?

Synchronization in C# can be achieved in multiple ways.

One of the ways to achieve Synchronization in C# is by using the feature of lock,


which locks the access to a block of code within the locked object.

When a thread locks an object, no other thread can access the block of code within
the locked object. Only when a thread releases the lock, then it is available for other
threads to access it.

In C# Language, every object has a built-in lock. By using the feature of


Synchronization, we can lock an object.

Locking an object can be done by using the lock keyword, and the following is the
syntax to use the lock.

lock(object)
{
//Statement1
//Statement2
//And more statements to be synchronized
}

So, when a thread acquires a lock over an object, then that particular thread can only
access the block of statements within the locked object.

11
Now, all the other threads wishing to access the same block of statements within the
same locked object will have to wait until, the thread that has got the lock on the
object, releases the lock, by exiting the block of statements.

Example without Thread Synchronization in C#:

Before we show you the example of how to use the synchronization between threads
by locking an object and its practical use,

let us first see what actually happens without using synchronization on executing
multiple threads, which are trying to access the same resource.

In the below example, we are creating three different threads that are going to going
to access the same resource i.e. in this case the shared resource is SomeMethod.
As you can see, we have delayed the SomeMethod execution by 1 second, and all
three threads trying to execute the same method and they will execute it.

The first thread which executes the method does not get its sole access, this thread
executes the method for a while and after some time, other threads will come and
execute the same method. So, here, we will not get the output as expected.

using System;
using [Link];
namespace ThreadStateDemo
{
class Program
{
static void Main(string[] args)
{
Thread thread1 = new Thread(SomeMethod)
{
Name = "Thread 1"
};
Thread thread2 = new Thread(SomeMethod)
{
Name = "Thread 2"
};
Thread thread3 = new Thread(SomeMethod)
{
Name = "Thread 2"
};
[Link]();
[Link]();
[Link]();
[Link]();

12
}
public static void SomeMethod()
{
[Link]("[Welcome To The ");
[Link](1000);
[Link]("World of Dotnet!]");
}
}
}
When I run the above code, I am getting the following output. The output might be
varied in your machine.

As you can see in the above output, here we are not getting the output as expected.

So, the point that you need to keep in mind is that if the shared resource is not
protected in a multithreaded environment from concurrent access, then the output or
the behavior of the application becomes inconsistent.

Example using Thread Synchronization in C#

In the below example, we are creating three threads that are going to access the
SomeMethod, but this time access to SomeMethod will be synchronized because we
are going to use the lock, to lock the object within which the method is going to be
accessed by multiple threads. The first thread to enter the method gets its sole
access until it exits the method, thereby avoiding the collision between multiple
threads trying to access a method.

using System;
using [Link];
namespace ThreadStateDemo
{
class Program
{
static object lockObject = new object();
static void Main(string[] args)
{
Thread thread1 = new Thread(SomeMethod)
{
Name = "Thread 1"
};
Thread thread2 = new Thread(SomeMethod)

13
{
Name = "Thread 2"
};
Thread thread3 = new Thread(SomeMethod)
{
Name = "Thread 2"
};
[Link]();
[Link]();
[Link]();
[Link]();
}
public static void SomeMethod()
{
// Locking the Shared Resource for Thread
Synchronization
lock (lockObject)
{
[Link]("[Welcome To The ");
[Link](1000);
[Link]("World of Dotnet!]");
}
}
}
}
Output:

Advertisements

The first thread enters the SomeMethod locked the method (code written within the
lock (object)), and gets its sole access and once this thread has finished its execution
of the method,

then only another thread will come and acquire a lock on the critical section i.e. the
code written within the lock object.

In this way, the lock object will make sure that at any given point in time, only one
thread can access the shared resource which will also ensure data consistency.

14
Realtime Example to Understand Thread Synchronization in C#:

Let us see one real-time example to understand thread synchronization and why it is
important to protect shared resources in our application. In the below example, I am
going to show you how to protect a shared variable from concurrent access in a
multithread environment.

In the below example, we are implementing the ticket booking functionality for a
movie. Let us assume the number of available tickets is 3 and three different threads
tried to book tickets. Thread1 tries to book 1 ticket, thread2 tries to book 2 tickets and
thread3 tries to book 3 tickets. Let us first see the problem without thread
synchronization. In the below code, we are not implementing thread synchronization
and hence sometime you will see that all three threads are able to book tickets. See,
the available tickets are 3 and we are able to book 6 tickets and this is the issue
without thread synchronization.

using System;
using [Link];
namespace ThreadStateDemo
{
class Program
{
static void Main(string[] args)
{
BookMyShow bookMyShow = new BookMyShow();
Thread t1 = new Thread([Link])
{
Name = "Thread1"
};
Thread t2 = new Thread([Link])
{
Name = "Thread2"
};
Thread t3 = new Thread([Link])
{
Name = "Thread3"
};
[Link]();
[Link]();
[Link]();
[Link]();
}
}
public class BookMyShow

15
{
int AvailableTickets = 3;
static int i = 1, j = 2, k = 3;
public void BookTicket(string name, int
wantedtickets)
{
if (wantedtickets <= AvailableTickets)
{
[Link](wantedtickets + " booked to "
+ name);
AvailableTickets = AvailableTickets -
wantedtickets;
}
else
{
[Link]("No tickets Available to
book");
}
}
public void TicketBookig()
{
string name = [Link];
if ([Link]("Thread1"))
{
BookTicket(name, i);
}
else if ([Link]("Thread2"))
{
BookTicket(name, j);
}
else
{
BookTicket(name, k);
}
}
}
}
Output:

16
As you can see in the above output, all the threads are able to book the tickets. This
is possible because all the threads are able to access the critical section code of the
program simultaneously. Now, let us proceed and see how we can restrict this i.e.
how we can allow only one thread to execute the critical section code.

Real-Time Example using Thread Synchronization in C#

In the below example, we have used the thread synchronization mechanism to lock
the critical section code by using the lock object. Now, the lock object will make sure
that only one thread can execute the critical section code, and once the thread
completes the execution of the critical section code, then another thread can enter
and execute the critical section code. With this, now you will get the output as
expected and you will see that you cannot book more than 3 tickets.

using System;
using [Link];
namespace ThreadStateDemo
{
class Program
{
static void Main(string[] args)
{
BookMyShow bookMyShow = new BookMyShow();
Thread t1 = new Thread([Link])
{
Name = "Thread1"
};
Thread t2 = new Thread([Link])
{
Name = "Thread2"
};
Thread t3 = new Thread([Link])
{
Name = "Thread3"
};
[Link]();
[Link]();
[Link]();
[Link]();
}
}
public class BookMyShow
{

17
private object lockObject = new object();
int AvailableTickets = 3;
static int i = 1, j = 2, k = 3;
public void BookTicket(string name, int
wantedtickets)
{
lock(lockObject)
{
if (wantedtickets <= AvailableTickets)
{
[Link](wantedtickets + " booked to "
+ name);
AvailableTickets = AvailableTickets -
wantedtickets;
}
else
{
[Link]("No tickets Available to
book");
}
}
}
public void TicketBookig()
{
string name = [Link];
if ([Link]("Thread1"))
{
BookTicket(name, i);
}
else if ([Link]("Thread2"))
{
BookTicket(name, j);
}
else
{
BookTicket(name, k);
}
}
}
}
Output:

18
So, by using the feature of synchronization (lock), we can avoid a conflict between
threads trying to access the same resource simultaneously. We can achieve thread
synchronization in C# by using the followings. From our next article onwards, we are
going to discuss the following in detail.

1. Lock
2. Monitor
3. Mutex
4. Semaphore
5. SemaphoreSlim

Note: Thread Synchronization in C# is a mechanism that ensures that two or more


concurrent processes or threads do not execute some particular section of the
program, especially the critical section. In this technique, one thread executes the
critical section of a program and the other thread waits until the thread finished its
execution. If a proper synchronization mechanism will be not applied then race
conditions will happen.

Why do we need Thread Synchronization in Multithreading?

We need Thread Synchronization in Multithreading because of the following:

1. Atomicity: Thread Synchronization supports atomicity, which ensures that


multiple threads in the application are not allowed to access a shared resource
concurrently to prevent data inconsistency. The code section of our program
which causes data inconsistency is known as the critical section. The critical
section of our program is executed atomically by one and only one thread
which ensures Atomicity. The Ticket booking example is an example of
Atomicity.
2. Ordering: We generally want two or more threads to perform a task in a
particular order or we want to restrict access to shared resources to a
particular number of threads only. Usually, we don’t have much control over all
this, which is one reason for race conditions. Thread synchronization provides
support for ordering so that you can have control over your threads to perform
the tasks as per your requirement. The first example that we discussed in this
article is the example of ordering.

What is Exclusive Lock and Non-Exclusive Lock in C#?

When a process or a thread wants to access an object, it requests a lock on that


object. There are two types of locks that determine access to shared resources –
Exclusive Lock and Non-Exclusive lock.

1. Exclusive Lock: An exclusive lock makes sure that only one thread can gain
access or enter a critical section at any given point in time. In C#, we can

19
implement Exclusive Lock using the lock keyword, Monitor class, Mutex Class,
and SpinLock class.
2. Non-Exclusive Lock: Non-Exclusive locks provide read-only access to a
shared resource and limit concurrency, i.e., limit the number of concurrent
accesses to a shared resource. In C#, we can implement Non-Exclusive Lock
using the Semaphore, SemaphoreSlim, and ReaderWriterLockSlim classes.

20

Common questions

Powered by AI

Using synchronization mechanisms like Monitor, Mutex, Semaphore, and SemaphoreSlim alongside lock in C# is beneficial for more complex thread management scenarios. These tools offer varied capabilities such as controlling access to resources across multiple processes (Mutex) or controlling access based on counting (Semaphore), which allows for flexibility and refined control over thread interactions and resource management, thus enhancing program robustness .

The lock keyword in C# provides a locking mechanism which ensures that a piece of code is not executed by more than one thread at a time. This keyword applies a mutual exclusion (mutex) to a block of code, preventing other threads from executing it and thus maintaining data integrity regardless of thread activity elsewhere in the application .

Thread synchronization using locks ensures that only one thread can access a critical section of code at any given time. By locking an object, other threads are prevented from entering a block of code until the lock is released. This prevents multiple threads from modifying shared resources simultaneously, thus avoiding data inconsistency .

The IsAlive property is used to determine the execution status of a thread. It returns true if the thread has started and has not yet completed its execution, allowing developers to thread-check if it is still running. This can be useful in scenarios where the main thread needs to conditionally execute code depending on whether a specific thread has finished executing .

The overloaded method Thread.Join(int milliseconds) is beneficial when you want the main thread to only wait for a specific duration for a thread to complete. For instance, if you want the main thread to wait a maximum of 3000 milliseconds for a child thread to complete a task and continue execution if it doesn't complete within this time, you can use this Join method overload. It allows timed waiting and helps in scenarios where responsiveness is critical .

The Thread.Sleep method temporarily pauses the execution of the thread for a specified number of milliseconds. This causes the current thread to 'sleep', meaning that it does not proceed with execution for the specified period, allowing other threads to execute during this time .

Without thread synchronization, issues like data races and inconsistencies arise because multiple threads may attempt to modify the same shared resource at the same time. This can lead to unpredictable results and errors, as demonstrated in scenarios like simultaneous ticket booking where more tickets than available are booked due to lack of synchronization .

Using Join methods ensures that the main thread waits for specific threads to complete their execution, thereby influencing output consistency by making sure that tasks are completed in the expected order without premature termination of the main thread. It helps to synchronize child threads with the main thread, providing a predictable execution pattern .

The Join method in the C# Thread class blocks the current thread until the thread on which Join is called has completed its execution. This is used to ensure that the main thread waits for child threads to complete before it continues execution, thus maintaining synchronization .

The lock(object) statement is pivotal in synchronizing access to shared resources in a multithreaded application. By applying a lock to an object, it ensures that the contained block of code is executed by only one thread at a time. This prevents race conditions and ensures data consistency by making threads wait to enter the critical section until it is free, thereby maintaining atomic operations .

You might also like