Ensuring the Mutual Exclusion of Shared Variables
in Pthreads |
Prof. David Bernstein |
Computer Science Department |
bernstdh@jmu.edu |
#include <pthread.h> #include <stdlib.h> #include <stdio.h> static volatile long global_counter = 0; static void* counter(void *arg) { long i, limit, local_counter; limit = *((long *) arg); for (i=0; i<limit; i++) { local_counter = global_counter; ++local_counter; global_counter = local_counter; // Try replacing the three statements above with the following //++global_counter; } return NULL; } int main(int argc, char *argv[]) { long limit; pthread_t thread_a, thread_b; if (argc <= 1) limit = 100; else limit = atol(argv[1]); // Start two threads counting pthread_create(&thread_a, NULL, counter, &limit); pthread_create(&thread_b, NULL, counter, &limit); // Wait for both threads to terminate pthread_join(thread_a, NULL); pthread_join(thread_b, NULL); // Display the result printf("Expected: %ld\n", limit*2); printf("Actual: %ld\n", global_counter); exit(0); }
thread_a
probably finishes before
thread_b
starts so the expected and actual
are the samethread_a
can read global_counter
,
assign its value to local_counter
, modify
local_counter
, and then get swapped-off
before it writes global_counter
thread_b
can then read global_counter
(which contains an incorrect value)++global_counter
is not atomic meaning the same race condition existspthread_mutex_t
variablespthread_mutex_t
variable becomes the "owner" of that variablepthread_mutex_t
variable can "unlock" (a.k.a. "release") itpthread_mutex_t
will block until it
is "unlocked"pthread_mutex_t
Variables)static pthread_mutex_t example = PTHREAD_MUTEX_INITIALIZER
PTHREAD_MUTEX_ERROR_CHECK
or
PTHREAD_MUTEX_RECURSIVE
, neither
of which is the default)EINVAL
EPERM
#include <pthread.h> #include <stdlib.h> #include <stdio.h> static volatile long global_counter = 0; static pthread_mutex_t counter_lock = PTHREAD_MUTEX_INITIALIZER; static void* counter(void *arg) { long i, limit, local_counter; limit = *((long *) arg); for (i=0; i<limit; i++) { pthread_mutex_lock(&counter_lock); // Start of Critical Section local_counter = global_counter; ++local_counter; global_counter = local_counter; // End of Critical Section pthread_mutex_unlock(&counter_lock); } return NULL; } int main(int argc, char *argv[]) { long limit; pthread_t thread_a, thread_b; if (argc <= 1) limit = 100; else limit = atol(argv[1]); // Start two threads counting pthread_create(&thread_a, NULL, counter, &limit); pthread_create(&thread_b, NULL, counter, &limit); // Wait for both threads to terminate pthread_join(thread_a, NULL); pthread_join(thread_b, NULL); // Display the result printf("Expected: %ld\n", limit*2); printf("Actual: %ld\n", global_counter); exit(0); }
pthread_mutex_lock()
pthread_mutex_trylock()
on the others. If
there are any errors release all of the locks, delay a random
amount of time, and try again.
PTHREAD_MUTEX_NORMAL
does not detect self-deadlock,
PTHREAD_MUTEX_ERRORCHECK
provides error
checking, and PTHREAD_MUTEX_RECURSIVE
uses a lock count