Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Creating Threads using the CreateThread() API

0.00/5 (No votes)
24 Mar 2006 2  
Creating threads using the CreateThread() API.

Sample Image

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.

The code

#include <windows.h> <WINDOWS.H>

#include <strsafe.h> <STRSAFE.H>

#include <stdio.h><STDIO.H>


#define BUF_SIZE 255

//------------------------------------------

// A function to Display the message

// indicating in which tread we are

//------------------------------------------

void DisplayMessage (HANDLE hScreen, 
     char *ThreadName, int Data, int Count)
{

    TCHAR msgBuf;
    size_t cchStringSize;
    DWORD dwChars;

    // Print message using thread-safe functions.

    StringCchPrintf(msgBuf, BUF_SIZE, 
       TEXT("Executing iteration %02d of %s" 
       " having data = %02d \n"), 
       Count, ThreadName, Data); 
    StringCchLength(msgBuf, BUF_SIZE, &cchStringSize);
    WriteConsole(hScreen, msgBuf, cchStringSize, 
                 &dwChars, NULL);
    Sleep(1000);
}

//-------------------------------------------

// A function that represents Thread number 1

//-------------------------------------------

DWORD WINAPI Thread_no_1( LPVOID lpParam ) 
{

    int     Data = 0;
    int     count = 0;
    HANDLE  hStdout = NULL;
    
    // Get Handle To screen.

    // Else how will we print?

    if( (hStdout = 
         GetStdHandle(STD_OUTPUT_HANDLE)) 
         == INVALID_HANDLE_VALUE )  
    return 1;

    // Cast the parameter to the correct

    // data type passed by callee i.e main() in our case.

    Data = *((int*)lpParam); 

    for (count = 0; count <= 4; count++ )
    {
       DisplayMessage (hStdout, "Thread_no_1", Data, count);
    }
    
    return 0; 
} 

//-------------------------------------------

// A function that represents Thread number 2

//-------------------------------------------

DWORD WINAPI Thread_no_2( LPVOID lpParam ) 
{

    int     Data = 0;
    int     count = 0;
    HANDLE  hStdout = NULL;
    
    // Get Handle To screen. Else how will we print?

    if( (hStdout = 
         GetStdHandle(STD_OUTPUT_HANDLE)) == 
         INVALID_HANDLE_VALUE )  
    return 1;

    // Cast the parameter to the correct

    // data type passed by callee i.e main() in our case.

    Data = *((int*)lpParam); 

    for (count = 0; count <= 7; count++ )
    {
      DisplayMessage (hStdout, "Thread_no_2", Data, count);
    }
    
    return 0; 
} 

//-------------------------------------------

// A function that represents Thread number 3

//-------------------------------------------

DWORD WINAPI Thread_no_3( LPVOID lpParam ) 
{
    int     Data = 0;
    int     count = 0;
    HANDLE  hStdout = NULL;

    // Get Handle To screen. Else how will we print?

    if( (hStdout = 
         GetStdHandle(STD_OUTPUT_HANDLE)) 
         == INVALID_HANDLE_VALUE )  
    return 1;

    // Cast the parameter to the correct

    // data type passed by callee i.e main() in our case.

    Data = *((int*)lpParam); 

    for (count = 0; count <= 10; count++ )
    {
     DisplayMessage (hStdout, "Thread_no_3", Data, count);
    }
    
    return 0; 
} 

 
void main()
{
    // Data of Thread 1

    int Data_Of_Thread_1 = 1;
    // Data of Thread 2

    int Data_Of_Thread_2 = 2;
    // Data of Thread 3

    int Data_Of_Thread_3 = 3;
    // variable to hold handle of Thread 1

    HANDLE Handle_Of_Thread_1 = 0;
    // variable to hold handle of Thread 1 

    HANDLE Handle_Of_Thread_2 = 0;
    // variable to hold handle of Thread 1

    HANDLE Handle_Of_Thread_3 = 0;
    // Aray to store thread handles 

    HANDLE Array_Of_Thread_Handles[3];

    // Create thread 1.

    Handle_Of_Thread_1 = CreateThread( NULL, 0, 
           Thread_no_1, &Data_Of_Thread_1, 0, NULL);  
    if ( Handle_Of_Thread_1 == NULL)
        ExitProcess(Data_Of_Thread_1);
    
    // Create thread 2.

    Handle_Of_Thread_2 = CreateThread( NULL, 0, 
           Thread_no_2, &Data_Of_Thread_2, 0, NULL);  
    if ( Handle_Of_Thread_2 == NULL)
        ExitProcess(Data_Of_Thread_2);
    
    // Create thread 3.

    Handle_Of_Thread_3 = CreateThread( NULL, 0, 
           Thread_no_3, &Data_Of_Thread_3, 0, NULL);  
    if ( Handle_Of_Thread_3 == NULL)
        ExitProcess(Data_Of_Thread_3);


    // Store Thread handles in Array of Thread

    // Handles as per the requirement

    // of WaitForMultipleObjects() 

    Array_Of_Thread_Handles[0] = Handle_Of_Thread_1;
    Array_Of_Thread_Handles[1] = Handle_Of_Thread_2;
    Array_Of_Thread_Handles[2] = Handle_Of_Thread_3;
    
    // Wait until all threads have terminated.

    WaitForMultipleObjects( 3, 
        Array_Of_Thread_Handles, TRUE, INFINITE);

    printf("Since All threads executed" 
           " lets close their handles \n");

    // Close all thread handles upon completion.

    CloseHandle(Handle_Of_Thread_1);
    CloseHandle(Handle_Of_Thread_2);
    CloseHandle(Handle_Of_Thread_3);
}

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.

A list of licenses authors might use can be found here