在多核处理器全面普及的当下,多线程编程早已成为提升程序并发性能、最大化利用系统硬件资源的必备技能。但多线程并发执行并非完美无缺 —— 多个线程同时争抢共享资源、线程执行顺序无序混乱,会直接引发数据错乱、逻辑异常、程序崩溃等难以复现的并发 bug。
而线程同步与互斥,就是解决这类并发问题的核心机制,更是 Linux 环境下高并发编程的基石。本文将从核心概念、底层实现、实战代码到避坑技巧,系统化拆解知识点,带你彻底吃透线程同步与互斥。
一、核心概念:一文分清同步与互斥
对于编程初学者,同步和互斥极易混淆,我们用生活化场景 + 专业定义双维度讲解,一目了然:
1. 线程互斥(Mutual Exclusion)
专业定义:互斥是一种排他性资源访问控制机制,用于解决多线程对共享资源的竞争问题。保证同一时刻仅有一个线程可以访问临界资源,杜绝并发修改导致的数据不一致。
例如:公共厕所

一个人进入后锁上门,其他人必须在门外排队等待,直到里面的人出来解锁,绝对不允许多人同时使用,这就是互斥。
2. 线程同步(Synchronization)
专业定义:同步是一种线程执行顺序协调机制,不限制资源独占,而是让多个线程按照预设的规则、顺序执行,实现线程间的有序协作。
例如:流水线作业

必须先完成原料加工,再进行产品组装,最后执行包装出库,线程间需要等待、通知,这就是同步。
3. 核心关系总结
✅ 互斥是特殊的同步,同步包含互斥
- 互斥:解决不能同时做的问题(资源竞争);
- 同步:解决要按顺序做的问题(执行协调)。
二、Linux 线程互斥实现方案(C 语言 pthread 库)
互斥的核心目标:独占临界资源,保证数据安全。Linux 环境下基于pthread库提供了完善的互斥实现,以下是最常用的两种方案:
方案 1:互斥锁(Mutex)—— 最基础、最通用的互斥机制
核心原理
线程访问共享资源前加锁,访问完成后解锁;未获取到锁的线程会进入阻塞状态,直到锁被释放,从根源避免资源竞争。
核心内容
NAME
pthread_mutex_init — destroy and initialize a mutex
销毁 和 初始化 互斥锁
SYNOPSIS
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
mutex:互斥锁
attr: 属性 👉 NULL 默认属性即可
int pthread_mutex_destroy(pthread_mutex_t *mutex); //销毁互斥锁
实战代码:奇偶判断 + 数值自增
#include <stdio.h>
#include <pthread.h>
// 定义互斥锁
pthread_mutex_t mutex;
// 共享资源
int value = 0;
// 子线程:判断偶数
void *fun(void *arg)
{
while (1)
{
// 加锁:进入临界区
pthread_mutex_lock(&mutex);
if (value % 2 == 0)
{
printf("%d 是偶数\n", value);
}
// 解锁:退出临界区
pthread_mutex_unlock(&mutex);
}
}
int main()
{
// 初始化互斥锁(默认属性)
pthread_mutex_init(&mutex, NULL);
pthread_t tid;
// 创建子线程
pthread_create(&tid, NULL, fun, NULL);
// 主线程:自增共享变量
while (1)
{
pthread_mutex_lock(&mutex);
value++;
pthread_mutex_unlock(&mutex);
}
// 回收资源(实际工程中需完善)
pthread_mutex_destroy(&mutex);
return 0;
}
方案 2:读写锁(R/W Lock)—— 读多写少场景的性能优化神器
核心原理
- 读锁(共享锁):允许多个线程同时加读锁,并发读取资源;
- 写锁(独占锁):同一时刻仅允许一个线程加写锁,写操作时阻塞所有读写操作;
- 适用场景:缓存读取、配置文件访问等读多写少业务,大幅提升并发效率。
核心内容
#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, pthread_rwlockattr_t *restrict attr); //初始化读写锁
rwlock:读写锁
attr:属性 默认为 NULL 即可
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); //销毁读写锁
实战代码:多线程读写共享缓冲区
#include <stdio.h>
#include <pthread.h>
#include <string.h>
// 定义读写锁
pthread_rwlock_t rwlock;
// 共享资源:缓冲区
char buf[1024] = {0};
// 写线程1
void *write_task1(void *arg)
{
while (1)
{
// 加写锁(独占)
pthread_rwlock_wrlock(&rwlock);
strcpy(buf, "你好世界");
// 解锁
pthread_rwlock_unlock(&rwlock);
}
}
// 写线程2
void *write_task2(void *arg)
{
while (1)
{
pthread_rwlock_wrlock(&rwlock);
strcpy(buf, "Hello from thread 2");
pthread_rwlock_unlock(&rwlock);
}
}
int main()
{
// 初始化读写锁
pthread_rwlock_init(&rwlock, NULL);
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, write_task1, NULL);
pthread_create(&thread2, NULL, write_task2, NULL);
// 主线程:循环读取
while (1)
{
// 加读锁(共享)
pthread_rwlock_rdlock(&rwlock);
printf("%s\n", buf);
pthread_rwlock_unlock(&rwlock);
}
// 资源回收
pthread_rwlock_destroy(&rwlock);
return 0;
}
三、Linux 线程同步实现方案
同步的核心目标:协调线程执行顺序,实现线程间有序通信。
方案 1:条件变量(Condition Variable)—— 线程等待 / 通知机制
核心原理
让线程主动进入等待状态,直到其他线程满足条件并发送唤醒信号,避免空循环浪费 CPU 资源,必须配合互斥锁使用。条件变量内部会对线程锁进行操作,所以使用条件变量时,必须添加线程锁。

条件变量的运行逻辑

条件变量函数接口
NAME
pthread_cond_destroy, pthread_cond_init — destroy and initialize condition variables
SYNOPSIS
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr); //初始化条件变量
cond:条件变量
attr:属性 默认为NULL 即可
int pthread_cond_destroy(pthread_cond_t *cond); //销毁条件变量
---------------👍进入等条件---------------
NAME
pthread_cond_timedwait, pthread_cond_wait — wait on a condition
SYNOPSIS
#include <pthread.h>
//设置等待的时长
int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);
cond:条件变量
mutex:线程锁
abstime:等待时间
//一直阻塞等待
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
cond:条件变量
mutex:线程锁
--------------👍唤醒等待线程--------------
NAME
pthread_cond_signal — signal a condition
SYNOPSIS
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);
cond:需要唤醒的条件
实战代码:按键唤醒线程
#include <stdio.h>
#include <pthread.h>
// 条件变量 + 互斥锁(必须搭配使用)
pthread_cond_t cond;
pthread_mutex_t mutex;
void *thread_func(void *arg)
{
while (1)
{
printf("进入等待条件,等待唤醒...\n");
// 阻塞等待信号
pthread_cond_wait(&cond, &mutex);
printf("线程被唤醒,执行任务!线程ID:%ld\n", pthread_self());
}
}
int main()
{
// 初始化
pthread_cond_init(&cond, NULL);
pthread_mutex_init(&mutex, NULL);
pthread_t tid;
pthread_create(&tid, NULL, thread_func, NULL);
// 主线程:输入按键唤醒子线程
while (1)
{
printf("按下回车唤醒线程工作\n");
getchar();
// 发送唤醒信号
pthread_cond_signal(&cond);
}
// 资源回收
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
return 0;
}
方案 2:信号量 PV 操作 —— 经典的同步互斥通用机制
核心原理
PV 操作是操作系统底层的同步互斥实现,通过信号量数值控制线程执行:
- P 操作(sem_wait):申请资源,信号量 - 1,值为 0 时线程阻塞;
- V 操作(sem_post):释放资源,信号量 + 1,唤醒阻塞线程。
#include <semaphore.h>
int sem_wait(sem_t *sem); // P操作 ,申请资源 -1
int sem_post(sem_t *sem); // V操作 ,释放资源 +1
int sem_getvalue(sem_t *sem, int *sval); //获取当前信号的值
-----------PV操作使用例子--------------
// 获取信号的值
int sval = 0;
sem_getvalue(sem, &sval);
printf("信号量的值为:%d\n", sval);
sem_wait(sem); // P操作 ,申请资源 -1
sem_getvalue(sem, &sval);
printf("信号量的值为:%d\n", sval);
--------------------------------------
// 获取信号的值
int sval = 0;
sem_getvalue(sem, &sval);
printf("信号量的值为:%d\n", sval);
sem_post(sem); // V操作 ,释放资源 +1
sem_getvalue(sem, &sval);
printf("信号量的值为:%d\n", sval);
--------------------------------------
互斥锁LVGL应用场景
LVGL 是单线程设计的 GUI 库,所有控件操作(如 lv_label_set_text)和界面刷新(lv_timer_handler)都必须在同一个线程中执行。当子线程直接调用 lv_label_set_text 时,会和主线程的 lv_timer_handler 发生资源竞争,触发断言错误:
_lv_inv_area: Asserted at expression: !disp->rendering_in_progress
✅ 标准解决方案(互斥锁版)
LVGL 默认不支持跨线程访问控件,如果要跨线程访问控件必须添加互斥锁.
四、并发编程避坑指南:死锁的预防
什么是死锁?
多个线程互相持有对方需要的锁,彼此无限等待,程序彻底卡死。
死锁预防三大原则
- 禁止嵌套加锁:一个线程不要同时持有多把锁;
- 固定加锁顺序:所有线程按照统一顺序获取锁,杜绝循环等待;
- 使用非阻塞锁:用
pthread_mutex_trylock替代阻塞加锁,避免永久等待。
五、编程最佳实践
- 锁粒度最小化:仅对共享资源操作加锁,减少线程阻塞时间;
- 禁止长时间持有锁:I/O、网络请求等耗时操作不要放在临界区;
- 锁必须配对使用:加锁后必须解锁,防止死锁;
- 场景化选型:简单数值用原子操作,资源独占用互斥锁,读多写少用读写锁,顺序协调用条件变量。
总结
线程同步与互斥是 Linux 多线程编程的核心命脉:
✅ 互斥守护共享资源安全,解决并发竞争问题;
✅ 同步协调线程执行顺序,实现高效线程协作;
✅ 互斥锁、读写锁、条件变量、PV 操作,是高并发编程的四大神兵利器。
多线程编程没有捷径,唯有理解原理、勤练代码、规避死锁,才能写出稳定、高效、健壮的并发程序。愿每一位开发者都能轻松驾驭多线程,在高并发的世界里乘风破浪!



1369

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



