POSIX Threads - CS 702 - Spring 2005

Thursday, September 30, 2004
Loyola College > Department of Computer Science > Dr. James Glenn > CS 702 > Examples and Lecture Notes > POSIX Threads

POSIX Threads

POSIX threads define a standard thread library implemented by many Unix systems.

In Linux, include pthread.h and compile with the -lpthread option to use POSIX threads.

Resources

Getting Started with POSIX Threads by Wagner and Towsley at UMass-Amherst.

Thread creation

Threads are created with the pthread_create function. pthread_create takes 4 parameters:

To terminate, a thread should call pthread_exit(void*) where the parameter is a pointer to a structure containing the return value of the thread, or NULL if no information need be returned.

pthread.cpp

#include <iostream>

#include <unistd.h>
#include <pthread.h>

/**
 * POSIX threads demo: starts two timer threads.
 *
 * Compile with
 * g++ -o pthreads.x pthreads.cpp -lpthread
 * if the -lpthread option is not specified the program will crash when run.
 */

/**
 * A structure to hold the arguments to the timer threads.
 */

struct timer_args
{
  int id;
  int ticks;
  int interval;
};

/**
 * A structure to hold the information returned from the timer threads.
 */

struct timer_result
{
  int time;
};

/**
 * The entry point for the timer threads.
 */

void *timer(void *p)
{
  timer_args *args = reinterpret_cast< timer_args * >(p);

  for (int tick = 0; tick < args->ticks; tick++)
    {
      sleep(args->interval);
      std::cout << "timer " << args->id << ": " << (tick + 1) * args->interval
		<< " seconds have elapsed" << std::endl;
    }

  timer_result *result = new timer_result;
  result->time = args->ticks * args->interval;

  delete args;

  return result;
}

int main()
{
  // set up arguments to pass to threads

  timer_args *args1 = new timer_args(), *args2 = new timer_args();
  args1->id = 1;
  args1->ticks = 10;
  args1->interval = 3;
  args2->id = 2;
  args2->ticks = 3;
  args2->interval = 10;

  // declare space for thread ids

  pthread_t timer1, timer2;

  // create threads

  pthread_create(&timer1, NULL, timer, args1);
  pthread_create(&timer2, NULL, timer, args2);
  
  // detach threads so memory used by the thread handler will be automatically
  // released when the threads terminate

  pthread_detach(timer1);
  pthread_detach(timer2);

  // exit main -- if main simply runs through to } the whole process terminates

  pthread_exit(NULL);
}

counter.cpp

#include <iostream>
#include <cstdlib>

#include <pthread.h>

/**
 * A demonstration of allowing threads unsynchronized access to
 * shared data structures.  Here we have two threads incrementing
 * a shared 64-bit counter.  If one thread is interrupted in the middle
 * of its increment, the count will be off.
 */

long long global = 0;
bool running = true;

void *counter(void *p)
{
  long long *count = reinterpret_cast< long long * >(p);

  long long local = 0;

  for (long long i = 0; i < *count; i++)
    {
      local++;

      // complicated way of incrementing a 64-bit int:
      // 1) increment the low word
      // 2) if the low word rolled over, increment the high word

      // split 64-bit int into two 32-bit words

      int low = *(reinterpret_cast< int * >(&global));
      int high = *(reinterpret_cast< int * >(&global + 1));
      
      // increment

      low++;
      if (low == 0)
	high++;

      // store result back

      *(reinterpret_cast< int * >(&global)) = low;
      *(reinterpret_cast< int * >(&global + 1)) = high;
    }

  long long *result = new long long(*count);
  delete count;

  return result;
}

int main(int argc, char **argv)
{
  if (argc < 2)
    {
      std::cerr << "USAGE: " << argv[0] << " n" << std::endl;
      return 1;
    }

  long long count = atoll(argv[1]);

  if (count < 0)
    {
      std::cerr << argv[0] << ": n must be positive" << std::endl;
      return 1;
    }

  // create arguments to pass to counter threads
    
  long long *count1 = new long long(count), *count2 = new long long(count);

  // create threads

  pthread_t counter1, counter2;
  pthread_create(&counter1, NULL, counter, count1);
  pthread_create(&counter2, NULL, counter, count2);

  // wait for threads to terminate

  long long *return1, *return2;
  pthread_join(counter1, reinterpret_cast< void ** >(&return1));
  pthread_join(counter2, reinterpret_cast< void ** >(&return2));

  // display results

  std::cout << "Threads preformed " << *return1 + *return2 << " increments"
	    << std::endl
	    << "Shared global counter is now " << global
	    << std::endl;

  // free memory allocated by threads for their return values

  delete return1;
  delete return2;

  return 0;
}
This code can also be downloaded from the files pthreads.cpp and counter.cpp.