Example: bank0.c
#include <stdio.h>
#include <pthread.h> // pthread_create, pthread_join
int balance = 0;
void* deposit(void *arg) {
for (int i = 0; i < 1e7; i++) {
++balance;
}
long r = 10 * (long)arg;
return (void *)r;
}
void* withdraw(void *arg) {
for (int i = 0; i < 1e7; i++) {
--balance;
}
long r = 10 * (long)arg;
return (void *)r;
}
int main() {
/* int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine)(void *),
void *arg);
Returns: 0 if OK, error number on failure */
pthread_t t1, t2;
pthread_create(&t1, NULL, &deposit, (void*)1);
pthread_create(&t2, NULL, &withdraw, (void*)2);
/* int pthread_join(pthread_t thread, void **retval);
Returns: 0 if OK, error number on failure */
void *r1;
void *r2;
pthread_join(t1, &r1);
pthread_join(t2, &r2);
printf("t1 returned %ld\n", (long)r1);
printf("t2 returned %ld\n", (long)r2);
printf("balance = %d\n", balance);
}
Processes vs. threads:
Thread synchronization problem:
i++
is not an atomic operation:
Inspect assembly code using objdump
Set CPU affinity using taskset
Use a mutex lock to synchronize the threads (bank1.c
):
int balance = 0;
pthread_mutex_t balance_lock = PTHREAD_MUTEX_INITIALIZER;
void* deposit(void *arg) {
for (int i = 0; i < 1e7; i++) {
pthread_mutex_lock(&balance_lock);
++balance;
pthread_mutex_unlock(&balance_lock);
}
long r = 10 * (long)arg;
return (void *)r;
}
void* withdraw(void *arg) {
for (int i = 0; i < 1e7; i++) {
pthread_mutex_lock(&balance_lock);
--balance;
pthread_mutex_unlock(&balance_lock);
}
long r = 10 * (long)arg;
return (void *)r;
}
POSIX Mutex API:
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
// Both return: 0 if OK, error number on failure
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
// All return: 0 if OK, error number on failure
#include <pthread.h>
#include <time.h>
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,
const struct timespec *restrict tsptr);
// Returns: 0 if OK, error number on failure
Deadlock condition:
Strict lock ordering avoids deadlock
See APUE 11.11 and 11.12 for examples of using two mutexes
POSIX Condition Variables API:
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);
// Both return: 0 if OK, error number on failure
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict tsptr);
// Both return: 0 if OK, error number on failure
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
// Both return: 0 if OK, error number on failure
Example from APUE 11.6.6:
#include <pthread.h>
struct msg {
struct msg *m_next;
/* ... more stuff here ... */
};
struct msg *workq;
pthread_cond_t qready = PTHREAD_COND_INITIALIZER;
pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER;
void process_msg(void)
{
struct msg *mp;
for (;;) {
pthread_mutex_lock(&qlock);
while (workq == NULL)
pthread_cond_wait(&qready, &qlock);
mp = workq;
workq = mp->m_next;
pthread_mutex_unlock(&qlock);
/* now process the message mp */
}
}
void enqueue_msg(struct msg *mp)
{
pthread_mutex_lock(&qlock);
mp->m_next = workq;
workq = mp;
pthread_cond_signal(&qready);
pthread_mutex_unlock(&qlock);
// In the textbook, the last two lines are written in reverse order:
//
// pthread_mutex_unlock(&qlock);
// pthread_cond_signal(&qready);
//
// Taking pthread_cond_signal() call out of the mutex region is
// allowed (and better) in this particular case, but it is not
// always safe to do so.
}
Condition Variables in Java:
Every class in Java extends java.lang.Object
java.lang.Object
contains 1 mutex and 1 condition variable
synchronized
keyword inserts mutex lock & unlock around a scope
class account {
int balance;
public synchronized void deposit() {
++balance;
}
public synchronized void withdraw() {
--balance;
}
}
wait()
, notify()
and notifyAll()
in java.lang.Object APILast updated: 2023-02-05