132 / 163 · C11 · 约 8 分钟
锁
先记住这句话
锁允许程序员保护临界区,确保共享数据的更新以原子方式发生,从而避免并发线程之间的竞态条件。
用锁保护共享更新
程序员在敏感代码前后调用锁获取和释放。这确保封闭操作对其他线程呈现为原子。锁变量记录其状态是空闲还是被单个线程持有。
细粒度与粗粒度锁定
单一全局锁会强制所有临界区串行执行。为不同数据结构使用独立锁可以提高并行度,让无关操作同时进行。
锁实现中的硬件与操作系统支持
高效锁依赖处理器提供的原子读写指令,以及操作系统在锁被占用时让线程休眠而非忙等的能力。
中断屏蔽方法的局限
关闭中断仅适用于单处理器,且赋予用户代码过多特权,恶意或错误程序可能使系统无法响应硬件事件。
常见误区
- 忘记解锁会使等待线程永远阻塞。
- 持锁期间执行可能睡眠的操作容易导致死锁。
运行一个例子
最低标准 C11 · 完整程序 · 下载 .c
#include <stdio.h>
#include <pthread.h>
static int shared_counter = 0;
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static void *worker(void *arg) { (void)arg;
(void)arg;
for (int i = 0; i < 5000; ++i) {
pthread_mutex_lock(&mtx);
++shared_counter;
pthread_mutex_unlock(&mtx);
}
return NULL;
}
int main(void) {
pthread_t t1, t2;
pthread_create(&t1, NULL, worker, NULL);
pthread_create(&t2, NULL, worker, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
printf("Shared counter is %d\n", shared_counter);
return 0;
}
在本地编译
gcc -std=c11 -Wall -Wextra -Wpedantic -Werror -pthread ostep-28-locks.c -o example && ./example预期结果
Shared counter is 10000
CHECK YOUR UNDERSTANDING
合上答案,试着解释。
为何仅用普通变量作为标志的锁无法保证互斥?
查看参考答案
测试标志是否空闲与随后将其设为占用这两步可能被调度打断,导致多个线程同时进入临界区。
继续查证
标准草案与官方章节会更新;版本标记只说明示例最低要求。