mutual exclusion: ensuring only one thread does a particular thing at a time
critical section: code that exactly one thread can execute at a time
lock: object only one thread can hold at a time
locks: an object with (at least) two operations:
typical usage: everyone acquires lock before using shared resource
Lock(account_lock);
balance += ...;
Unlock(account_lock);
#include <pthread.h>
pthread_mutex_t account_lock;
pthread_mutex_init(&account_lock, NULL);
// or: pthread_mutex_t account_lock =
// PTHREAD_MUTEX_INITIALIZER;
...
pthread_mutex_lock(&account_lock);
balance += ...;
pthread_mutex_unlock(&account_lock);
...
pthread_mutex_destroy(&account_lock);
pthread_mutex_t lock1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lock2 = PTHREAD_MUTEX_INITIALIZER;
string one = "init one", two = "init two";
void ThreadA() {
pthread_mutex_lock(&lock1);
one = "one in ThreadA"; // (A1)
pthread_mutex_unlock(&lock1);
pthread_mutex_lock(&lock2);
two = "two in ThreadA"; // (A2)
pthread_mutex_unlock(&lock2);
}
void ThreadB() {
pthread_mutex_lock(&lock1);
one = "one in ThreadB"; // (B1)
pthread_mutex_lock(&lock2);
two = "two in ThreadB"; // (B2)
pthread_mutex_unlock(&lock2);
pthread_mutex_unlock(&lock1);
}possible values of one/two after A+B run?
B1+A2
NOT A1+B2
pthread_mutex_t lock1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lock2 = PTHREAD_MUTEX_INITIALIZER;
string one = "init one", two = "init two";
void ThreadA() {
pthread_mutex_lock(&lock2);
two = "two in ThreadA"; // (A2)
pthread_mutex_unlock(&lock2);
pthread_mutex_lock(&lock1);
one = "one in ThreadA"; // (A1)
pthread_mutex_unlock(&lock1);
}
void ThreadB() {
pthread_mutex_lock(&lock1);
one = "one in ThreadB"; // (B1)
pthread_mutex_lock(&lock2);
two = "two in ThreadB"; // (B2)
pthread_mutex_unlock(&lock2);
pthread_mutex_unlock(&lock1);
}possible values of one/two after A+B run?
pthread_mutex_t lock1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lock2 = PTHREAD_MUTEX_INITIALIZER;
string one = "init one", two = "init two";
void ThreadA() {
pthread_mutex_lock(&lock2);
two = "two in ThreadA"; // (A2)
pthread_mutex_unlock(&lock2);
pthread_mutex_lock(&lock1);
one = "one in ThreadA"; // (A1)
pthread_mutex_unlock(&lock1);
}
void ThreadB() {
pthread_mutex_lock(&lock1);
one = "one in ThreadB"; // (B1)
pthread_mutex_unlock(&lock1);
pthread_mutex_lock(&lock2);
two = "two in ThreadB"; // (B2)
pthread_mutex_unlock(&lock2);
}possible values of one/two after A+B run?
lots of coordinating threads beyond locks
will talk about two general tools later:
big added feature: wait for arbitrary thing to happen
also some less general tools: barriers
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; bool ready = false;
void WaitForReady() {
pthread_mutex_lock(&lock);
do {
pthread_mutex_unlock(&lock);
/* only time MarkReady() can run */
pthread_mutex_lock(&lock);
} while (!ready);
pthread_mutex_unlock(&lock);
}
void MarkReady() {
pthread_mutex_lock(&lock);
ready = true;
pthread_mutex_unlock(&lock);
}DO NOT USE THIS CODE
in practice: want more than locks for synchronization
for waiting for arbtirary events (without CPU-hogging-loop):
for common synchornization patterns:
higher-level interface:
Object MilkLock = new Object();
/* lock implicity acquired/released on
entering/leaving this block */
synchronized (MilkLock) {
if (no milk) {
buy milk
}
}
#include <mutex>
std::mutex MilkLock;
{
std::lock_guard nameDoesNotMatter(MilkLock);
/* nameDoesNotMatter's constructor acquires lock */
if (no milk) {
buy milk
}
/* nameDoesNotMatter's destructor called automatically
and releases lock
*/
}
mutex attributes (pthread_mutexattr_t) allow:
man pthread.h)error-checking mutexes
mutexes shared between processes
…
#include <pthread.h>
pthread_mutex_t some_lock;
pthread_mutex_init(&some_lock, NULL);
// or: pthread_mutex_t some_lock = PTHREAD_MUTEX_INITIALIZER;
...
pthread_mutex_lock(&some_lock);
...
pthread_mutex_unlock(&some_lock);
pthread_mutex_destroy(&some_lock);
can you use a vector from multiple threads?
… question: how is it implemented?
can access from multiple threads … as long as not append/erase/etc.?
assuming it’s implemented like we expect…
multiple threads can read anything at the same time
can only read element if no other thread is modifying it
can safely add/remove elements if no other threads are accessing container
exception: vectors of bools — can’t safely read and write at same time