一、互斥锁的概念及理解
互斥锁(Mutex,全称 Mutual Exclusion Lock)是 Linux 多线程编程中用于保护共享资源的一种同步机制。它确保在任意时刻,只有一个线程可以访问特定的共享资源或代码段(即临界区),从而避免数据竞争和不一致问题。互斥锁本质上是一种二进制锁,只有两种状态:锁定(Locked)和解锁(Unlocked)。
核心特性:
- 原子性:加锁和解锁操作是原子的,操作系统保证同一时间只有一个线程能成功加锁。
- 唯一性:一旦线程锁定互斥锁,其他线程在锁被释放前无法再次锁定。
- 非忙等待:若锁已被占用,其他线程会进入睡眠状态(不占用 CPU 资源),直到锁被释放后由内核唤醒。
互斥锁的工作原理
互斥锁内部通常包含:
-
锁状态:锁定/未锁定
-
等待队列:存储等待获取锁的线程
-
所有者:当前持有锁的线程ID
类比理解:
以卫生间为例,当一个人进入卫生间并锁门(加锁)后,其他人无法进入(阻塞);当此人离开并解锁后,其他人可尝试进入,但一次只能进入一人。
二、互斥锁的使用方法
典型流程:
- 初始化互斥锁:动态或静态初始化。
- 加锁:线程访问共享资源前调用
pthread_mutex_lock。 - 访问共享资源:执行临界区代码。
- 解锁:线程完成操作后调用
pthread_mutex_unlock。 - 销毁互斥锁:不再使用时调用
pthread_mutex_destroy(动态初始化的锁需手动销毁)。

代码示例:
#include <pthread.h>
#include <stdio.h>
pthread_mutex_t mutex; // 定义互斥锁
int shared_resource = 0; // 共享资源
void* thread_func(void* arg) {
pthread_mutex_lock(&mutex); // 加锁
shared_resource++; // 访问共享资源
printf("Thread %ld: shared_resource = %d\n", (long)arg, shared_resource);
pthread_mutex_unlock(&mutex); // 解锁
return NULL;
}
int main() {
pthread_t t1, t2;
// 初始化互斥锁(动态初始化)
if (pthread_mutex_init(&mutex, NULL) != 0) {
perror("pthread_mutex_init failed");
return 1;
}
// 创建线程
pthread_create(&t1, NULL, thread_func, (void*)1);
pthread_create(&t2, NULL, thread_func, (void*)2);
// 等待线程结束
pthread_join(t1, NULL);
pthread_join(t2, NULL);
// 销毁互斥锁
pthread_mutex_destroy(&mutex);
return 0;
}
输出:
Thread 1: shared_resource = 1
Thread 2: shared_resource = 2
三、互斥锁的注意事项
-
加锁与解锁必须成对出现:
遗漏解锁会导致其他线程永久阻塞,引发死锁。 -
避免嵌套加锁:
非递归锁(默认类型)不允许同一线程多次加锁,否则会死锁。递归锁(PTHREAD_MUTEX_RECURSIVE_NP)支持同一线程多次加锁,但需谨慎使用。 -
防止死锁:
- 固定加锁顺序:多个锁时,所有线程按相同顺序请求锁。
- 超时机制:使用
pthread_mutex_trylock或pthread_mutex_timedlock避免无限等待。 - 示例死锁场景:
pthread_mutex_t mutex1, mutex2;
void* threadA(void* arg) {
pthread_mutex_lock(&mutex1);
sleep(1); // 模拟工作
pthread_mutex_lock(&mutex2); // 死锁:线程B已持有mutex2并等待mutex1
// ...
}
void* threadB(void* arg) {
pthread_mutex_lock(&mutex2);
sleep(1); // 模拟工作
pthread_mutex_lock(&mutex1); // 死锁
// ...
}
-
性能考虑:
过度使用互斥锁会导致线程频繁阻塞和唤醒,降低并发性能。应尽量缩小临界区范围。 -
错误处理:
检查所有互斥锁 API 的返回值,确保操作成功。
四、互斥锁相关 API 概要总结
| API 名称 | 功能 | 初始化/销毁 | 加锁/解锁 | 非阻塞加锁 | 超时加锁 |
|---|---|---|---|---|---|
| 函数 | pthread_mutex_initpthread_mutex_destroy | pthread_mutex_lockpthread_mutex_unlock | pthread_mutex_trylock | pthread_mutex_timedlock | |
| 属性控制 | 支持(通过 pthread_mutexattr_t) | - | - | - |
五、详细 API 介绍
1. pthread_mutex_init
功能:动态初始化互斥锁。
函数原型:
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
参数:
mutex:指向互斥锁变量的指针。attr:指向互斥锁属性的指针。若为NULL,使用默认属性(普通非递归锁)。
返回值:
- 成功:返回
0。 - 失败:返回错误码(如
ENOMEM内存不足,EINVAL参数无效)。
示例:
pthread_mutex_t mutex;
if (pthread_mutex_init(&mutex, NULL) != 0) {
perror("pthread_mutex_init failed");
}
2. pthread_mutex_destroy
功能:销毁互斥锁,释放其占用的资源。
函数原型:
int pthread_mutex_destroy(pthread_mutex_t *mutex);
参数:
mutex:指向待销毁的互斥锁变量的指针。
返回值:
- 成功:返回
0。 - 失败:返回错误码(如
EBUSY锁仍被占用,EINVAL参数无效)。
注意事项:
-
确保锁未被任何线程持有
-
确保没有线程在等待该锁
-
静态初始化的互斥锁不需要销毁
示例:
pthread_mutex_destroy(&mutex);
3. pthread_mutex_lock
功能:对互斥锁加锁。若锁已被占用,调用线程阻塞直到锁被释放。
行为描述:
-
如果锁可用,立即获得锁并返回
-
如果锁被其他线程持有,则调用线程阻塞直到锁可用
-
同一线程重复加锁可能导致死锁(普通锁)
函数原型:
int pthread_mutex_lock(pthread_mutex_t *mutex);
参数:
mutex:指向互斥锁变量的指针。
返回值:
- 成功:返回
0。 - 失败:返回错误码(如
EINVAL锁无效,EDEADLK检测到死锁)。
示例:
pthread_mutex_lock(&mutex);
// 临界区代码
pthread_mutex_unlock(&mutex);
4. pthread_mutex_unlock
功能:释放互斥锁,允许其他线程加锁。
函数原型:
int pthread_mutex_unlock(pthread_mutex_t *mutex);
参数:
mutex:指向互斥锁变量的指针。
返回值:
- 成功:返回
0。 - 失败:返回错误码(如
EINVAL锁无效,EPERM线程未持有锁)。
注意事项:
-
必须由锁的持有者调用
-
解锁后等待队列中的一个线程将被唤醒
-
不要解锁未锁定的互斥锁
示例:
pthread_mutex_unlock(&mutex);
5. pthread_mutex_trylock
功能:非阻塞尝试加锁。若锁已被占用,立即返回错误码 EBUSY 而非阻塞。
行为特点:
-
尝试获取锁,但不会阻塞
-
成功获取返回0
-
锁不可用时立即返回EBUSY
函数原型:
int pthread_mutex_trylock(pthread_mutex_t *mutex);
参数:
mutex:指向互斥锁变量的指针。
返回值:
- 成功:返回
0。 - 失败:返回错误码(如
EBUSY锁被占用,EINVAL锁无效)。
使用场景:
-
避免死锁
-
实现轮询机制
示例:
if (pthread_mutex_trylock(&mutex) == 0) {
// 加锁成功,执行临界区代码
pthread_mutex_unlock(&mutex);
} else {
// 加锁失败,处理其他逻辑
}
6. pthread_mutex_timedlock
功能:在指定时间内尝试加锁。若超时仍未获取锁,返回错误码 ETIMEDOUT。
函数原型:
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
参数:
mutex:指向互斥锁变量的指针。abstime:指向timespec结构的指针,指定超时时间(绝对时间)。
返回值:
- 成功:返回
0。 - 失败:返回错误码(如
ETIMEDOUT超时,EINVAL参数无效)。
示例:
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 2; // 设置超时时间为当前时间 + 2秒
if (pthread_mutex_timedlock(&mutex, &ts) == 0) {
// 加锁成功
pthread_mutex_unlock(&mutex);
} else {
// 加锁失败(超时)
}
六、互斥锁属性(pthread_mutexattr_t)
通过 pthread_mutexattr_t 可自定义互斥锁行为,常见属性类型包括:
PTHREAD_MUTEX_NORMAL:普通锁,非递归,检错行为依赖实现。PTHREAD_MUTEX_ERRORCHECK:检错锁,同一线程重复加锁返回错误。PTHREAD_MUTEX_RECURSIVE:递归锁,同一线程可多次加锁,需对应次数解锁。PTHREAD_MUTEX_DEFAULT:默认锁,行为依赖实现(通常为普通锁)。
设置属性示例:
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); // 设置为递归锁
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, &attr);
pthread_mutexattr_destroy(&attr); // 销毁属性对象
七、使用互斥锁的注意事项
1. 避免死锁的黄金法则
-
固定加锁顺序:所有线程按相同顺序获取多个锁
-
尝试加锁:使用
pthread_mutex_trylock避免无限等待 -
锁超时:使用
pthread_mutex_timedlock设置等待上限 -
锁粒度:锁的粒度要尽可能小,持有时间尽可能短
2. 常见错误及预防
| 错误类型 | 后果 | 预防措施 |
|---|---|---|
| 忘记解锁 | 死锁 | 使用RAII模式,确保解锁 |
| 重复加锁 | 死锁 | 使用错误检查锁或递归锁 |
| 解锁未持有锁 | 未定义行为 | 检查锁的所有权 |
| 破坏锁变量 | 未定义行为 | 避免修改已初始化的锁 |


被折叠的 条评论
为什么被折叠?



