Multi-Threaded Programming With POSIX Threads

  1. What are pthreads?
  2. Creating and destroying pthreads


What are pthreads?

Historically, hardware vendors have implemented their own proprietary versions of threads. These implementations differed substantially from each other, making it difficult for programmers to develop portable threaded applications.

In order to take full advantage of the capabilities provided by threads, a standardized programming interface was required. For UNIX systems, this interface has been specified by the IEEE POSIX 1003.1c standard (1995). Implementations which adhere to this standard are referred to as POSIX threads, or Pthreads. Most hardware vendors now offer Pthreads in addition to their proprietary threads.


Creating and destroying threads

When a multi-threaded program starts executing, it has one thread running, which executes the main() function of the program. This is already a full-fledged thread, with its own thread ID. In order to create a new thread, the program should use the pthread_create() function. Here is how to use it:



#include <stdio.h>       /* standard I/O routines                 */
#include <pthread.h>     /* pthread functions and data structures */

/* function to be executed by the new thread */
void* PrintHello(void* data)
{
    int my_data = (int)data;     	/* data received by thread */

    printf("Hello from new thread - got %d\n", my_data);
    pthread_exit(NULL);			/* terminate the thread */
}

/* like any C program, program's execution begins in main */
int main(int argc, char* argv[])
{
    int        rc;         	/* return value                           */
    pthread_t  thread_id;     	/* thread's ID (just an integer)          */
    int        t         = 11;  /* data passed to the new thread          */

    /* create a new thread that will execute 'PrintHello' */
    rc = pthread_create(&thread_id, NULL, PrintHello, (void*)t);  
    if(rc)			/* could not create thread */
    {
        printf("\n ERROR: return code from pthread_create is %d \n", rc);
        exit(1);
    }
    printf("\n Created new thread (%d) ... \n", threadid);
    
    pthread_exit(NULL);		/* terminate the thread */
}

Understanding the simple threaded program above. While it does not do anything useful, it will help you understand how threads work. Let us take a step by step look at what the program does.

  1. In main() we declare a variable called thread_id, which is of type pthread_t. This is basically an integer used to identify the thread in the system. After declaring thread_id, we call the pthread_create() function to create a real, living thread.

  2. pthread_create() gets 4 arguments The first argument is a pointer to thread_id, used by pthread_create() to supply the program with the thread's identifier. The second argument is used to set some attributes for the new thread. In our case we supplied a NULL pointer to tell pthread_create() to use the default values. Notice that PrintHello() accepts a void * as an argument and also returns a void * as a return value. This shows us that it is possible to use a void * to pass an arbitrary piece of data to our new thread, and that our new thread can return an arbitrary piece of data when it finishes. How do we pass our thread an arbitrary argument? Easy. We use the fourth argument to the pthread_create() call. If we do not want to pass any data to the new thread, we set the fourth argument to NULL. pthread_create() returns zero on success and a non-zero value on failure.

  3. After pthread_create() successfully returns, the program will consist of two threads. This is because the main program is also a thread and it executes the code in the main() function in parallel to the thread it creates. Think of it this way: if you write a program that does not use POSIX threads at all, the program will be single-threaded (this single thread is called the "main" thread).

  4. The call to pthread_exit() causes the current thread to exit and free any thread-specific resources it is taking. There is no need to use this call at the end of the thread's top function, since when it returns, the thread would exit automatically anyway. This function is useful if we want to exit a thread in the middle of its execution.

In order to compile a multi-threaded program using gcc, we need to link it with the pthreads library. Assuming you have this library already installed on your system, here is how to compile our first program:

      gcc hello.c -o hello -lpthread
The source code for this program may be found in the hello.c file.


Exercise. Download hello.c into your Unix account. Compile the source code and run the hello executable. The ouput should be similar to
      Created new thread (4) ...
      Hello from new thread - got 11