Linux 线程互斥原理与实战
在多线程编程中,线程互斥是解决共享资源竞争的核心机制。想象一下多个线程同时操作同一个余票变量,结果可能卖出负数车票;或者修改全局变量时状态混乱。这些都是数据竞争引发的典型问题,而互斥锁就是保护临界资源的'金钥匙'。
核心概念:共享与临界资源
理解互斥前,先明确几个基础概念。
共享资源与临界资源
多线程程序中,线程间通过共享数据交互,这些被共同访问的数据称为共享资源。例如售票系统的 ticket 变量、电商库存 stock。
并非所有共享资源都需要保护。临界资源是指那些不能被多个线程同时访问的资源,一旦并发访问会导致数据不一致。比如 ticket 变量是临界资源,因为多个线程同时执行'判断大于 0→打印→减 1'的操作时会出错;而线程栈上的局部变量只属于当前线程,无需保护。
临界区
每个线程内部访问临界资源的代码段即为临界区。非临界区的代码(如日志打印、局部计算)可并发执行。
if (ticket > 0) {
usleep(1000);
printf("%s sells ticket:%d\n", id, ticket);
ticket--;
}
这段直接操作 ticket 的代码就是典型的临界区。
原子性要求
有效的互斥要求操作具备原子性:操作要么完成,要么未完成,不存在中间状态。操作系统调度是随机的,若操作非原子,执行一半被抢占就会导致状态混乱。这也是多线程出问题的根本原因——对临界资源的操作往往不是原子的。
多线程共享资源的坑
光说概念不够直观,我们看一个经典的多线程售票系统案例。
问题代码:未加互斥的售票系统
创建 4 个线程同时操作全局变量 ticket(初始值 100):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
int ticket = 100;
void *route(void *arg) {
char *id = (char*)arg;
while (1) {
(ticket > ) {
usleep();
(, id, ticket);
ticket--;
} {
;
}
}
;
}
{
t1, t2, t3, t4;
pthread_create(&t1, , route, );
pthread_create(&t2, , route, );
pthread_create(&t3, , route, );
pthread_create(&t4, , route, );
pthread_join(t1, );
pthread_join(t2, );
pthread_join(t3, );
pthread_join(t4, );
;
}


