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.
void* thread1(void*); void* thread2(void*);
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; }
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; }
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.
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); }
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
.
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);
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); ... }
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); ...
quit
to terminate the loop in thread1()
a thread2()
functions, e.g.,
void* thread1(void *v) { ... while (!q) { ... pthread_mutex_lock(&mtx); ... q = quit; ....
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()
.
condvar
for the conditional variable and initialize it using default attributes, e.g.,
pthread_cond_t condvar; ... pthread_cond_init(&condvar, NULL);
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); ...
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); ...
stdout
in the output thread by fflush(stdout);
main()
function.
Enter
switch the terminal to the raw
mode, e.g., using one of the technique presented in lec06.
"\rAlarm period: %10i Alarm counter: %10i"