线程锁 基础
锁的侧重点在于 资源的保护。
互斥锁 mutex
互斥锁 是用于互斥的。当资源的访问出现竞争的时候,则需考虑用 互斥锁。
互斥锁 的状态
互斥锁 有两种状态:
互斥锁 有两种操作:
操作
描述
加锁
加锁
解锁
解锁
互斥锁 的状态跳转表:
原状态
加锁
解锁
未锁定状态
互斥锁 进入锁定状态
互斥锁 保持未锁定状态
状态
描述
未锁定状态
锁定状态
只有一个线程可以占有 互斥锁
原状态
加锁
解锁
锁定状态
本线程 进入阻塞等待
互斥锁 进入未锁定状态
互斥锁 静态初始化
POSIX 定义了一个宏 PTHREAD_MUTEX_INITIALIZER 来静态初始化 互斥锁:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
互斥锁 动态初始化
pthread_mutex_init() 用来初始化 互斥锁。
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
其中 attr 用于指定互斥锁属性,如果为 NULL 则使用缺省属性。
互斥锁 销毁
pthread_mutex_destroy() 用于销毁 互斥锁。
int pthread_mutex_destroy(pthread_mutex_t *mutex);
销毁互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态。
互斥锁 加锁
pthread_mutex_lock() 用于 互斥锁 加锁。
int pthread_mutex_lock(pthread_mutex_t *mutex);
1. 如果 互斥锁 没有加锁。pthread_mutex_lock 对该 互斥锁 加锁。最后返回 0。
2. 如果 互斥锁 已经加锁。pthread_mutex_lock 则会阻塞当前线程。直到 互斥锁 释放。
注意:
同一线程连续多次 pthread_mutex_lock ,会造成死锁。
pthread_mutex_trylock() 用于 互斥锁 加锁。但是不会阻塞。
int pthread_mutex_trylock(pthread_mutex_t *mutex);
1. pthread_mutex_trylock 与 pthread_mutex_lock 相似。
2. 如果 互斥锁 已经加锁。pthread_mutex_trylock 则不会阻塞线程。而是立即返回 错误码。
互斥锁 解锁
pthread_mutex_unlock 用于 互斥锁 解锁。
int pthread_mutex_unlock(pthread_mutex_t *mutex);
读写锁 read-write lock
读写锁,用于并发读取和独占写入。适合在读取的次数大于写入的场合。
读写锁 的状态
读写锁 有三种状态:
状态
描述
未锁定状态
读锁定状态
多个线程可以同时占有 读锁
写锁定状态
只有一个线程可以占有 写锁
读写锁 有三种操作:
操作
描述
读加锁
读加锁
写加锁
写加锁
解锁
读解锁 或者 写解锁
互斥锁 的状态跳转表:
原状态
读加锁
写加锁
解锁
原状态
读加锁
写加锁
解锁
未 锁 定
状态
取得 读锁,读写锁 进入 读锁定
状态
取得 写锁,读写锁 进
入 进入写锁定状态
出现未定义问题
读 锁 定
状态
如果已经有 写锁 在阻塞等待,
则 本线程阻塞;
否则取得 读锁,读写锁 进入
读锁定状态
本线程阻塞,并且 读
写锁 阻塞随后的 读加
锁 操作
如果这是最后的读锁,则 读写
锁 进入 未锁定状态;
否则 读写锁 进入 读锁定状态
写 锁 定
状态
本线程阻塞
本线程阻塞
读写锁 进入 未锁定状态
读写锁 初始化
pthread_rwlock_init() 用于初始化 读写锁。正常初始化后 读写锁会在 未加锁的状态。
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
const pthread_rwlockattr_t *restrict attr);
其中 attr 用于指定 读写锁 属性,如果为 NULL 则使用缺省属性。
使用 没有初始化 或者 多次初始化的 读写锁,都会出现不确定的问题。
读写锁 销毁
pthread_rwlock_destroy 用于销毁 读写锁。
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);