线程控制:互斥与同步
互斥(Mutex)
概念
在多线程环境中,互斥用于保证对临界资源 (如全局变量、文件、设备)的排他性访问 。当多个线程并发执行时(如 th1 和 th2 交替运行),对共享资源的非原子操作(如 a++ 对应的多步汇编指令)可能导致数据不一致。互斥锁通过将临界区代码封装为原子操作(一次线程调度中必须完整执行),确保同一时刻仅有一个线程访问资源。
使用步骤
-
定义互斥锁
cpthread_mutex_t mutex; -
初始化锁
cint pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);attr通常设为NULL(默认属性)。- 返回值:成功返回
0,失败返回非零值。
-
加锁
cint pthread_mutex_lock(pthread_mutex_t *mutex);- 若锁已被占用,则阻塞当前线程。
- 临界区代码需短小,避免耗时操作或休眠。
-
解锁
cint pthread_mutex_unlock(pthread_mutex_t *mutex); -
销毁锁
cint pthread_mutex_destroy(pthread_mutex_t *mutex);
同步(Semaphore)
概念
同步是互斥的扩展,用于控制线程执行的先后顺序。与互斥锁不同:
- 互斥锁:加锁和解锁由同一线程完成。
- 信号量 :线程可交叉释放资源(如
th1释放th2的资源)。 - 计数信号量 :初值可大于
1(如资源池),支持多线程并发访问。
使用步骤
-
定义信号量
csem_t sem; -
初始化信号量
cint sem_init(sem_t *sem, int pshared, unsigned int value);pshared=0:线程间共享;pshared≠0:进程间共享。value:初始资源数(如二值信号量设为1)。
-
P/V 操作
-
P 操作(申请资源) :
cint sem_wait(sem_t *sem); // 资源不足时阻塞 -
V 操作(释放资源) :
cint sem_post(sem_t *sem); // 释放后信号量值 +1
-
-
销毁信号量
cint sem_destroy(sem_t *sem);
死锁
产生条件
死锁由以下四个必要条件同时满足导致:
- 互斥条件:资源仅能被一个线程占用。
- 请求与保持:线程阻塞时仍持有已获取的资源。
- 不可剥夺:资源在使用完前不能被强制释放。
- 循环等待:线程间形成环形资源依赖链。
预防策略
- 破坏任一条件即可避免死锁,例如:
- 按固定顺序申请锁。
- 设置锁超时机制。
互斥 vs. 信号量
| 特性 | 互斥锁 | 信号量 |
|---|---|---|
| 资源数量 | 单一资源 | 可多资源(计数信号量) |
| 加解锁主体 | 同一线程 | 可跨线程交叉释放 |
| 适用场景 | 短临界区 | 允许休眠或耗时操作 |