Warning
This page is located in archive. Go to the latest version of this course pages. Go the latest version of this page.

Lab06 - Multi-thread programming

The goal is to implement a program with three threads running simultaneously, where the first thread is devoted for processing input (reading pressed keys), the second thread is dedicated for updating output (single line output), the third thread implements an timer, which increments a variable after a given period, which can be controlled by the user input. In this case, pthread (POSIX threads) library is used. To do that, implement simple blocks demonstrating usage of pthread library.

First threads

  • You can use a template project lab06.zip
  • Create two function to encapsulate body of individual threads

   void* thread1(void*);
   void* thread2(void*);

  • In thread1(), implement a loop in which a global value counter is incremented after 100 ms, e.g.,

   void* thread1(void *v)
   { 
      bool q = false;  /* from the <stdbool.h> */
      while (!q) {
         usleep(100 * 1000);  /* from the <unistd.h> */
         counter += 1;
      }
      return 0;
   }

  • In thread2(), implement a loop to update a line with the current value of the counter, e.g.,

   void* thread2(void *v) 
   {
      bool q = false;
      while (!q) {
         printf(*"\rCounter %10i", counter);
      }
      return 0;
   }

  • Create threads using the pthread_create() function and passing the pointer to the particular function thread1() and thread2() in the main function, e.g.,

   counter = 0;
 
   pthread_t thrs[2];
   pthread_create(&thrs[0], NULL, thread1, NULL);
   pthread_create(&thrs[1], NULL, thread2, NULL);

Once the thread is created, it is started and it goes to the passed function, which executed immediately.
 
  • Wait for pressing Enter by the user and then wait for the threads by calling pthread_join(), e.g.,

   getchar();
   for (int i = 0; i < 2; ++i) {
      pthread_join(thrs[i], NULL);
   }

Use mutex for protecting critical section

In general, whenever we access to shared variable from multiple threads it is desirable to protect the access by a critical section, a lock that will allow only a single thread to accessing to the section. This can be implemented using mutex, i.e., pthread_mutex_t.

  • Create a global variable mtx and initialize it using by the pthread_mutex_init() function with a default attributies, e.g.,

   pthread_mutex_t mtx;
   pthread_mutex_init(&mtx, NULL);

  • Initialize the mutex before its usage in the particular threads. Notice, threads may be executed sooner than you expect.
  • Protected access to the global variable counter by the critical section using pthead_mutex_lock() and pthread_mutex_unlock(), e.g.,

   void *thread1(void *v)
   {
      ...
      pthread_mutex_lock(&mtx);
      counter += 1;
      pthread_mutex_unlock(&mtx);
      ...
   }   

  • Introduce a new global variable quit to indicate program termination, once the user pressed Enter, e.g.,

   bool quit = false;
   ...
   gechar();
   pthread_mutex_lock(&mtx);
   quit = true;
   pthread_mutex_unlock(&mtx);
   ...

  • Use the variable quit to terminate the loop in thread1() a thread2() functions, e.g.,

   void* thread1(void *v)
   {
      ...
      while (!q) {
      ...
      pthread_mutex_lock(&mtx);
      ...
      q = quit;
      ....

Use conditional variable to signal other threads

The current implementation of the printing loop in thread2 is computationally intensive as it is executed as fast as possible using all the available cpu resources. This is not efficient and we would like to update the line only when the variable is changed. Such a synchronization mechanism can be implemented by using conditional variable, the type pthread_cond_t, “suspend” use the execution of a thread by calling pthread_cond_wait() and signal the “suspended” thread by signaling it using pthread_cond_singnal() or pthread_cond_broadcast().

  • Define global variable condvar for the conditional variable and initialize it using default attributes, e.g.,

    pthread_cond_t condvar;
    ...
    pthread_cond_init(&condvar, NULL);

  • The execution of the thread can be suspended by calling pthread_cond_wait(), which release locked mutex and wait for the signal on the conditional variable. Once it is received, it will reacquire the mutex and continue with the thread execution. Update body of the thread2(), e.g.,

   void* thread2(void *d)
   {
      ...
      pthread_mutex_lock(&mtx);
      ...
      pthread_cond_wait(&condvar, &mtx);
      ...
      pthread_mutex_unlock(&mtx);
      ...

  • Signal the thread2() using pthread_cond_signal() once the counter is incremented, e.g.,

   void* thread1(void *d)
   {
      ...
      pthread_mutex_lock(&mtx);
      counter += 1;
      ...
      pthread_cond_signal(&condvar);
      ...
      pthread_mutex_unlock(&mtx);
      ...

  • It may be needed to flush the stdout in the output thread by
    fflush(stdout);

Exercise

  • Use the above building blocks to create an application with three threads. In fact, there will be four threads including the main thread for the main() function.
  • To read press keys without pressing Enter switch the terminal to the raw mode, e.g., using one of the technique presented in lec06.
  • Use a structure to encapsulate all the shared data variables
  • Protected access to the shared data using critical section (mutex)
  • Signal the threads to avoid waste of computational resources
  • Add a name to each thread to be identified by a string: “Input”, “Output”, and “Alarm”
  • The “Alarm” thread incrementally increases the alarm counter after the alarm period.
  • Consider arrays and data structures that will allow you to automate managing threads using for loops.
  • The program will react to the following pressed keys
    • 'q' - quits the program, all threads are correctly terminated
    • 'r' - decreases the period of increasing counter by 10 ms
    • 'p' - increases the period of increasing counter by 10 ms
  • The minimal value of the period is 10 ms
  • The maximal value of the period is 2000 ms
  • The output of the program is a single line as
    "\rAlarm period: %10i   Alarm counter: %10i"
courses/be5b99cpl/labs/lab06.txt · Last modified: 2018/10/04 15:47 (external edit)