线程同步机制
Table of Contents
调研⌗
https://blog.devgenius.io/os-how-mutex-is-implemented-concurrency-part-4-addd9f8eabbe⌗
中断是 CPU 在线程之间切换的重要方法。
class Mutex{
...
void lock(){
interrupt_disable();
if(locked){
// put current thread to wait queue.
}else{
locked = true;
}
interrupt_enable();
}
void unlock(){
interrupt_disable();
if(/* the owner of the lock is current thread*/){
locked = free;
}
// pull some threads from wait queue and put it into ready queue
// so that that thread which was waiting for this mutex can start working
interrupt_enable();
}
}
In lock(), we should first disable the interrupt, so that no thread can modify the state “locked.” If the mutex is already locked, we should put the current thread to the Wait Queue, if the lock is free, we set the locked to true, enable the interrupt and return. It is somewhat similar to using lock and unlock to protect shared states in the user’s code. We can think of interrupt as a low-level mechanism to prevent the shared state, “locked.” When we unlock() it, we should pull something out of the wait queue according to its scheduler’s priority, and put that thread into the Ready Queue so that the thread which was waiting for the mutex can acquire a lock and do work.
互斥量⌗
当一个线程可以修改的变量,其他线程也可以读取或者修改的时候,我们就需要对这些线程进行同步,确保他们在访问变量的存储内容时不会访问到无效的值。
在使用 Mutex 时,如果分别对两个资源使用了不同的 Mutex,需要确保多个线程获取资源时以相同的顺序,否则会发生线程死锁(环路等待条件)。
#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);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
读写锁⌗
读写锁与 Mutex 类似,但其允许更高的并行性。Mutex 要么是锁住状态,要么是不加锁状态,而且一次只有一个线程可以对齐进行加锁。读写锁则可以有三种状态:读模式下加锁状态、写模式下加锁状态、不加锁状态。一次只有一个线程可以占有写模式的读写锁,但是多个线程可以同时占有读模式的读写锁。
读写锁非常适合于对数据结构读的次数远大于写的情况。
#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
const pthread_rwlockattr_t *restrict attr);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); // 加读锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); // 加写锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
条件变量⌗
条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定的条件发生。
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
int pthread_cond_destro(pthread_cond_t *cond);
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);
pthread_cond_wait
进行以下处理:
- mutex 会对 cond 进行保护
- pthread_cond_wait 内部自动把调用线程放到等待条件的线程列表上
- 对互斥量 mutex 进行解锁
- pthread_cond_wait 返回后(被唤醒),mutex 再次被加锁
唤醒:
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
自旋锁⌗
自旋锁与互斥量类似,但它不通过休眠使进程阻塞,而是在获取锁之前一直处于忙等待阻塞状态。自旋锁主要用在:锁被持有的事件很短且线程并不希望在重新调度上增加太多的成本。
自旋锁可以使用 TAS(Test and Set) 指令进行实现。
#include <pthread.h>
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
int pthread_spin_destroy(pthread_spinlock_t *lock);
int pthread_spin_lock(pthread_spinlock_t *lock);
int pthread_spin_trylock(pthread_spinklock_t *lock);
int pthread_spin_unlock(pthread_spinklock_t *lock);
屏障⌗
屏障用于协调多个线程并行的工作,屏障使得每个线程等待,直到所有的合作线程都到达某一点,然后从该点继续执行。pthread_join
是一种屏障。
#include <pthread.h>
int pthread_barrier_init(pthread_barrier_t *restrict barrier,
const pthread_barrierattr_t *restrict attr,
unsigned int count);
int pthread_barrier_destroy(pthread_barrier_t *barrier);
int pthread_barrier_wait(pthread_barrier_t *barrier);