Synchronization¶
Race Conditions (1)¶
Suppose static int global;
void inc()
{
global++;
}
|
|
Race Conditions (2)¶
Imagine more complex data structures (linked lists, trees): if incrementing a dumb integer bears a race condition, then what can we expect in a multithreaded world?
No single data structure of C++’s Standard Template Library is thread safe
std::string
’s copy constructor and assignment operator are thread safe (GCC’s Standard C++ Library ⟶ not by standard)std::string
’s other methods are not thread safestdio and iostream are thread safe (by standard since C++11)
Mutex (1)¶
int pthread_mutex_init(pthread_mutex_t *mutex,
const pthread_mutexattr_t *attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
Dynamic initialization using
pthread_mutex_init()/``pthread_mutex_destroy()
attr == NULL
⟶ default mutex (⟶ later)Static initialization using
PTHREAD_MUTEX_INITIALIZER
Mutex (2)¶
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
Simple lock/unlock must be enough
If you find yourself using “trylock”, then something’s wrong*
Polling is never right!
Mutex (3)¶
static pthread_mutex_t global_mutex =
PTHREAD_MUTEX_INITIALIZER;
static int global;
void inc()
{
/* error handling omitted */
pthread_mutex_lock(&global_mutex);
global++;
pthread_mutex_unlock(&global_mutex);
}
Mutex Types¶
int pthread_mutexattr_settype(
pthread_mutexattr_t *attr, int type);
PTHREAD_MUTEX_NORMAL
: no checks, no nothing. Same thread locks mutex twice in a row before unlock ⟶ Deadlock.PTHREAD_MUTEX_ERRORCHECK
: Deadlock check; unlocking a mutex locked by another thread ⟶ ErrorPTHREAD_MUTEX_RECURSIVE
: owner can lock same mutex twicePTHREAD_MUTEX_DEFAULT
⟶PTHREAD_MUTEX_NORMAL
Atomic Instructions¶
Simple integers don’t need a mutex
static int global;
void inc()
{
__sync_fetch_and_add(&global, 1);
}
More ⟶ info gcc
, GCC manual