9.3. Multi-threaded Programming

Parallel programming using fork() has several limitations.

  • fork() is expensive. Memory is copied from the parent to the child and all the descriptors are copied.
  • The overall system is slowed by extra process switching. The same amount of work can be done faster with less processes running on the system. As the number of running processes increases, the operating system has more work that it must do and less CPU resources are available for user level processes.
  • Interprocess communication is awkward and limited in functionality. Signals and pipes are the only means for independent processes to communicate with each other.

In the threads model, multiple lines of execution are maintained in a single process.

  • Threads are created faster than processes are created with fork().
  • Multi-threaded programs require less process switching because if one thread is blocked, another thread may have work to do. Thus compared to a single thread process, a multi-thread process is less likely to be completely blocked.
  • Separate threads of the same program share global data and have some thread specific mechanism for signaling one another.

Unfortunately, there are multiple versions of the threads library calls. The most common are the POSIX or pthreads package and Solaris threads.

The main concepts with threads programming is thread creation and synchronization. Since multiple threads can modify the same global data, we need to coordinate the threads so that only one thread at a time can modify global data. Two types of system calls are mostly used to coordinate a thread, semaphores and conditional signals.

9.3.1. examples

Here is a basic threads program, except no thread coordination is used.

/*-------------------------------------------------------------*/
/* thread1.c - create threads that interact...                */
/*              compile using: gcc thread1.c -lthread         */
/*-------------------------------------------------------------*/
#include <stdio.h>
#include <thread.h>
#include <synch.h>
#include <time.h>

void *do_something(void *);  /* new thread's code */

int x = 0;                   /* global variable */

main(int argc, char **argv)
{
    int num_threads = 5;
    thread_t tid;
    int i;

    /* create worker threads */
    for (i=1; i<=5; i++)
    {
          thr_create(NULL,0,do_something,NULL,0,&tid);
          printf("Created thread with id %d \n",tid);
    }

    /* wait for children to exit */
    while (thr_join(0, NULL, NULL) == 0);

    printf("final value: x = %d.\n", x);
    exit(0);
}

void *do_something(void *arg)
{
    int tid;         /* thread id */
    int sleep_time;
    int i, num_loops;
    int y = 0; /* local variable */

    tid = thr_self();        /* who am i */

    srand((long) time(0)+tid);  /* seed random number generator */

    num_loops = rand()%5 + 1;   /* loop for 1 to 5 times */

    for (i=1; i<=num_loops; i++)
    {
         printf("in %d, loop number %d of %d, x = %d, y = %d.\n",
                tid,i,num_loops,x,y);
         y=x+1;
         sleep(rand()%3+1);
         x=y;
    }
    return;
}

Here we fix the above program by using semaphores.

/*-------------------------------------------------------------*/
/* thread2.c - synchronize thread interaction...              */
/*-------------------------------------------------------------*/
#include <stdio.h>
#include <thread.h>
#include <synch.h>
#include <time.h>
#include <synch.h>

void *do_something(void *);  /* new thread's code */

int x = 0;                   /* global variable */
sema_t mutex;                /* semaphore */

main(int argc, char **argv)
{
    int num_threads = 5;
    thread_t tid;
    int i;

    /* initialize semaphore */
    sema_init(&mutex,1,USYNC_THREAD,NULL);

    /* create worker threads */
    for (i=1; i<=5; i++)
    {
          thr_create(NULL,0,do_something,NULL,0,&tid);
          printf("Created thread with id %d \n",tid);
    }

    /* wait for children to exit */
    while (thr_join(0, NULL, NULL) == 0);

    printf("final value: x = %d.\n", x);
    exit(0);
}

void *do_something(void *arg)
{
    int tid;         /* thread id */
    int i, num_loops;
    int y = 0;       /* local variable */

    tid = thr_self();       /* who am i */

    srand((long) time(0)+tid); /* seed random number generator */

    num_loops = rand()%5 + 1;   /* loop for 1 to 5 times */

    for (i=1; i<=num_loops; i++)
    {
        sema_wait(&mutex);
        printf("in %d, loop number %d of %d, x = %d.\n",
                    tid,i,num_loops,x);
        y=x+1;
        sleep(rand()%3+1);
        x=y;
        sema_post(&mutex);
        sleep(rand()%3+1);
    }
    return;
}