|
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