Introduction

This article is meant for beginners who want to take initial lessons in creating threads. This article explains how to create threads using the CreateThread() function. When I started to learn multithreaded programming, I had a very hard time finding simple and descriptive programs and articles that would demonstrate and explain concepts related to multithreading in simple plain English. And this is what motivated me to write my first article on multithreading, for CodeProject.

Background

I am presenting a program which will demonstrate the creation and concurrent execution of three threads using the Windows API CreateThread().

Using the code

I have provided the source code in a zip file source.zip. I have provided the executable "Test1.exe" in Test1.zip. When you run Test1.exe from the Windows command prompt, you will see the output in a DOS box as shown in the picture above.

Explanation of the code

Our goal is to create and concurrently execute three threads using Windows API CreateThread(). Let the three threads be Thread_no_1, Thread_no_2, and Thread_no_3. Each thread is represented by a function. So let's name the functions. The function corresponding Thread_no_1 is named as Thread_no_1(). The function corresponding to Thread_no_2 is named as Thread_no_2(). The function corresponding Thread_no_3 is named as Thread_no_3(). Thus, we have three functions, each representing one specific thread. Thus, when we say three threads running concurrently, we mean three functions are executing concurrently. In other words, when we say three threads Thread_no_1, Thread_no_2, and Thread_no_3 are running concurrently, we mean three functions Thread_no_1(), Thread_no_2(), and Thread_no_3() are being executed concurrently. Therefore, we have defined three functions in our program. The three functions are:

Thread_no_1()

Thread_no_2()

Thread_no_3()

Each thread works on a piece of data fed to it or on globally available data. In our case, we are not using any global data. So threads in our example will use the data fed to them. And the thread in our program is represented by a function. So feeding data to a thread means feeding data to a function. How do we feed data to a function? By passing arguments to a function. Thus, our functions that represent the threads accept the data through arguments. So now, every function that represents the thread looks like this...

Thread_no_1(LPVOID lpParam)

Thread_no_2(LPVOID lpParam)

Thread_no_3(LPVOID lpParam)

where "LPVOID lpParam" is a long pointer to a void.

Who must be passing data to these threads, i.e., to the thread functions? Well....it's done by the Windows API CreateThread(). How this is done will be discussed shortly. Wonder who calls the CreateThread() API? It's the main() program that calls the CreateThread() API to simply create a thread. And where does the program execution begin from? It begins from main(). Thus, main() calls CreateThread(). The function CreateThread() creates threads. Threads execute concurrently and terminate. That's the story!

Let us now understand the implementation of the threads, i.e., the thread functions. Consider the first thread function, Thread_no_1.

Thread_no_1():

The prototype of the function is "DWORD WINAPI Thread_no_1( LPVOID lpParam )". No questions on this please! It has to be like this. Let's accept it at a beginner's level. The function accepts the data fed to it in the form of a long void pointer variable lpParam. It defines two integer variables, Data and count. It defines a variable hStdout of data type HANDLE. Next, the function gets a handle to the screen (standard output) using the function "GetStdHandle()". The thread function returns if it fails to obtain a handle to the screen. Next, the function extracts the data from lpParam and stores it in the variable Data. This data was passed to Thread_no_1() through the function CreateThread() called by main(). Next, the function implements a for loop that runs four times. The function DisplayMessage() is called from within the for loop. So the function DisplayMessage executes four times. Its job is to display a string which indicates the thread under execution, the iteration number, and the data that was passed to the thread.

Thread_no_2():

The prototype of the function is "DWORD WINAPI Thread_no_2( LPVOID lpParam )". No questions on this please! It has to be like this. Let's accept it at a beginner's level. The function accepts the data fed to it in the form of a long void pointer variable lpParam. It defines two integer variables, Data and count. It defines a variable hStdout of data type HANDLE. Next, the function gets a handle to the screen (standard output) using the function "GetStdHandle()". The thread function returns if it fails to obtain a handle to the screen. Next, the function extracts the data from lpParam and stores it in the variable Data. This data was passed to Thread_no_2() through the function CreateThread() called by main(). Next, the function implements a for loop that runs seven times. The function DisplayMessage() is called from within the for loop. So the function DisplayMessage executes seven times. Its job is to display a string which indicates the thread under execution, the iteration number, and the data that was passed to the thread.

Thread_no_3():

The prototype of the function is "DWORD WINAPI Thread_no_3( LPVOID lpParam )". No questions on this please! It has to be like this. Let's accept it at a beginner's level. The function accepts the data fed to it in the form of a long void pointer variable lpParam. It defines two integer variables, Data and count. It defines a variable hStdout of data type HANDLE. Next, the function gets a handle to the screen (standard output) using the function "GetStdHandle()". The thread function returns if it fails to obtain a handle to the screen. Next, the function extracts the data from lpParam and stores it in the variable Data. This data was passed to Thread_no_3() through the function CreateThread() called by main(). Next, the function implements a for loop that runs 10 times. The function DisplayMessage() is called from within the for loop. So the function DisplayMessage executes ten times. Its job is to display a string which indicates the thread under execution, the iteration number, and the data that was passed to the thread.

Now, let's consider the function DisplayMessage().

DisplayMessage():

This function returns nothing. This function accepts four parameters. The first parameter is a handle to the screen, the second parameter is the name of the thread, the third parameter is the data of the thread, and the last parameter is the iteration number the thread is executing. This function declares a buffer msgbuff to hold the message to display on the screen. The function declares cchStringSize to hold the size of the message to be displayed. The function declares dwChars as a DWORD. It is required by the WriteConsole function. The function StringCchPrintf() copies the message to be displayed in msgbuff. The function StringCchLength() calculates the length of the message to be displayed. The function WriteConsole() displays the message. After displaying the message, it sleeps for a second.

To reiterate, the job of every thread in our program is to display a message after every second.

Over to the main() program.

Main Program:

The goal of the main program is to create three threads and let them run concurrently till they terminate. A thread is created using the CreateThread() function. When the CreateThread() function creates a thread, it returns a thread handle. Our main program has to store this handle. Since we are creating three threads, our program needs to store three thread handles, so our program defines three handle variables viz. Handle_Of_Thread_1, Handle_Of_Thread_2, and Handle_Of_Thread_3. Our program also defines three data variables which will contain the data to be passed to the threads. Thus, the contents of the variable Data_Of_Thread_1 will be passed to first thread, i.e., Thread_no_1(). The contents of the variable Data_Of_Thread_2 will be passed to the second thread, i.e., Thread_no_2(), and the contents of the variable Data_Of_Thread_3 will be passed to the third thread, i.e., Thread_no_3. Next, we declare an array to hold the three thread handles. The name of the array is Array_Of_Thread_Handles[3]. The purpose of this array will be discussed shortly.

Next, an attempt is made to create the first thread by making a call to the function CreateThread(). CreateThread accepts six parameters. Our goal is to create a simple thread. So, we will focus on the third and fourth parameters of the CreateThread() function. The address of the function Thread_no_1() is passed as the third parameter. The address of the variable Data_Of_Thread_1 is passed as the fourth parameter. This is the way we pass data to the thread. The CreateThread() function creates a thread and the thread starts executing. The function CreateThread() returns Thread_no_1's handle. This handle is collected in the handle variable Handle_Of_Thread_1. If a NULL value is returned, the program exits with the exit value of Data_Of_Thread_1.

Next, an attempt is made to create the second thread, by making a call to the function CreateThread(). Our goal is to create a simple thread. The address of the function Thread_no_2() is passed as the third parameter. The address of the variable Data_Of_Thread_2 is passed as the fourth parameter. This is the way we pass data to the thread. The CreateThread() function creates a thread and the thread starts executing. The function CreateThread() returns Thread_no_2's handle. This handle is collected in the handle variable Handle_Of_Thread_2. If a NULL value is returned, the program exits with the exit value of Data_Of_Thread_2.

Next, an attempt is made to create the third thread by making a call to the function CreateThread(). The address of the function Thread_no_3() is passed as the third parameter. The address of the variable Data_Of_Thread_3 is passed as the fourth parameter. This is the way we pass data to the thread. The CreateThread() function creates a thread and the thread starts executing. The function CreateThread() returns Thread_no_3's handle. This handle is collected in the handle variable Handle_Of_Thread_3. If a NULL value is returned, the program exits with the exit value of Data_Of_Thread_3.

At this point, all the three threads are executing concurrently, and you get to see the output on the screen as shown in the picture above. Thread_no_1 has a for loop that loops four times. Thread_no_2 has a for loop that loops seven times. Thread_no_3 has a for loop that loops ten times. So, Thread_no_1 will complete first, Thread_no_2 will complete next, and Thread_no_3 will be the last to complete.

WaitForMultipleObjects()

Now that all the three threads are executing concurrently, you get to see the output on the screen as shown in the picture above. We wait to let all the threads execute, and then and then only, should we exit from our program. In order to do so, we need to call the function WaitForMultipleObjects(). To this function, we need to pass the handles for all the threads on which we wish to wait. This function demands that we store these handles in an array and pass this array as the second argument to the function WaitForMultipleObjects(). So we define an array Array_Of_Thread_Handles[3]. We store the handles of the three threads in this array and pass this array as the second argument to the function WaitForMultipleObjects(). The first argument to this function indicates that we are waiting on the three threads. The third parameter is TRUE. It indicates a wait for all the threads. The last parameter is passed as INFINITE. This means, wait till all threads have completed.

Thus at this point, the main program waits for all the threads to complete their execution. In the mean time, the threads are running concurrently as is seen from the picture above. When all the threads are done, the control is transferred to the main program. The program prints the statement "Since all threads executed, let's close their handles" and goes ahead and closes the handles of the threads and exits.

I hope this program makes the creation of threads clear. Let me know if you have any questions. I hope I would be able to answer them.

History

March 19, 2006: Initial release.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

You call the CreateThread() API putting the results into the discrete values, then copy them into the array, then call WaitForMultipleObjects() on the array. Why don't you just use the array by itself without the discrete values and subsequent handle copy?

Sorry, but I don't really want to see an article preaching the usage of CreateThread. And I really don't want to see someone using CreateThread where they should be using _beginthreadex, or whatever is the equivalent, based on the compiler/environment (for example, AfxBeginThread if using MFC) they're using. I'm in total agreement with technoway. Using CreateThread is just wrong!

...the sixth and final parameter of CreateThread() must not be null under Windows 9x operating systems as it expects to write the thread ID to wherever the parameter points to, no matter if it's null or not.

CloseHandle takes a HANDLE rather than a HANDLE* or HANDLE&. It cannot actually change the value of the handle, although of course manually nulling a closed handle to prevent accidental re-use is common practice.

I have been reading and reading tutorials about this topic from all over the internet. Each of them leaving me more and more confused. Eventually I decided to stick to single threaded applications.

Your article is excellent, it gets right to the point and is well commented. Just two suggestions, in your next article maybe you could cut back on the copy and paste a little. Describing one function in detail and a few side notes for the others would make a much better read. And second, in your next article please discuss more about communicating with threads, passing and retrieving data, and more importantly waiting for a thread to finish.

Unfortunately, Microsoft made a mistake when they implemented the WIN32 CreateThread function. Threads created with this can leak C-runtime memory. If no C-runtime calls are used, then no leaks will occur.

Microsoft created _beginthread and _endthread to fix this. Unfortunately, they made another mistake. _endthread closes the thread handle. Because of that, it's not possible to wait on a thread handle created with _beginthread (with either WaitForSingleObject or WaitForMultipleObjects) because the handle will have been closed if the thread already exited (or the handle will be closed while waiting on it).

To fix this, Microsoft created _beginthreadex and _endthreadex. These are the preferred functions to use because they will work properly in all circumstances.