在linux系统下的多线程编程场景中,多个线程并发执行时如果同时访问和修改共享资源,很容易出现数据不一致、逻辑错误等问题,线程同步就是用来解决这类问题的核心手段,通过协调线程的执行顺序,保证共享资源在同一时间只被一个或一组线程安全访问。

linux多线程同步的常见方式
1. 互斥锁(mutex)
互斥锁是最基础的线程同步方式,它的核心逻辑是同一时间只允许一个线程持有锁,其他尝试获取锁的线程会被阻塞,直到持有锁的线程释放锁。互斥锁适合保护需要互斥访问的共享资源,比如全局变量、共享内存区域等。
使用互斥锁需要先初始化锁,然后在访问共享资源前后分别加锁和解锁,最后销毁锁。下面是pthread库互斥锁的使用示例:
#include <stdio.h>
#include <pthread.h>
// 定义共享资源和互斥锁
int shared_num = 0;
pthread_mutex_t mutex;
// 线程执行函数
void* thread_func(void* arg) {
for (int i = 0; i < 10000; i++) {
// 加锁
pthread_mutex_lock(&mutex);
shared_num++;
// 解锁
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main() {
pthread_t t1, t2;
// 初始化互斥锁
pthread_mutex_init(&mutex, NULL);
// 创建两个线程
pthread_create(&t1, NULL, thread_func, NULL);
pthread_create(&t2, NULL, thread_func, NULL);
// 等待线程结束
pthread_join(t1, NULL);
pthread_join(t2, NULL);
// 输出结果,预期是20000
printf("shared_num: %dn", shared_num);
// 销毁互斥锁
pthread_mutex_destroy(&mutex);
return 0;
}
2. 条件变量(condition_variable)
条件变量通常和互斥锁配合使用,用于线程之间的条件等待和通知。当一个线程需要满足某个条件才能继续执行时,它可以等待在条件变量上,另一个线程在条件满足时通知等待的线程。条件变量适合生产者消费者这类场景,比如一个线程生产数据,另一个线程等待数据就绪后再处理。
下面是条件变量配合互斥锁实现简单生产者消费者的示例:
#include <stdio.h>
#include <pthread.h>
// 定义共享数据、互斥锁、条件变量
int data = 0;
int has_data = 0;
pthread_mutex_t mutex;
pthread_cond_t cond;
// 生产者线程函数
void* producer_func(void* arg) {
for (int i = 0; i < 5; i++) {
pthread_mutex_lock(&mutex);
data = i;
has_data = 1;
printf("生产者生产数据: %dn", data);
// 通知等待条件变量的线程
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
// 消费者线程函数
void* consumer_func(void* arg) {
for (int i = 0; i < 5; i++) {
pthread_mutex_lock(&mutex);
// 等待条件满足,会自动释放锁,被唤醒后重新获取锁
while (has_data == 0) {
pthread_cond_wait(&cond, &mutex);
}
printf("消费者消费数据: %dn", data);
has_data = 0;
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main() {
pthread_t producer, consumer;
// 初始化互斥锁和条件变量
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
// 创建生产者和消费者线程
pthread_create(&producer, NULL, producer_func, NULL);
pthread_create(&consumer, NULL, consumer_func, NULL);
// 等待线程结束
pthread_join(producer, NULL);
pthread_join(consumer, NULL);
// 销毁互斥锁和条件变量
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
3. 信号量(semaphore)
信号量是一个计数器,用于控制同时访问共享资源的线程数量。它支持两种操作,分别是P操作(计数器减1,若计数器为0则阻塞)和V操作(计数器加1,唤醒阻塞的线程)。信号量既可以用于互斥(初始值为1时和互斥锁效果类似),也可以用于控制并发数量,比如限制同时只有3个线程访问某个资源。
下面是使用信号量控制并发访问数量的示例,限制同时只有2个线程执行任务:
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
// 定义信号量
sem_t sem;
// 线程执行函数
void* thread_func(void* arg) {
// P操作,获取信号量
sem_wait(&sem);
printf("线程 %ld 开始执行任务n", (long)arg);
// 模拟任务执行
sleep(1);
printf("线程 %ld 任务执行完成n", (long)arg);
// V操作,释放信号量
sem_post(&sem);
return NULL;
}
int main() {
pthread_t threads[5];
// 初始化信号量,初始值为2,最多允许2个线程同时访问
sem_init(&sem, 0, 2);
// 创建5个线程
for (int i = 0; i < 5; i++) {
pthread_create(&threads[i], NULL, thread_func, (void*)(long)i);
}
// 等待所有线程结束
for (int i = 0; i < 5; i++) {
pthread_join(threads[i], NULL);
}
// 销毁信号量
sem_destroy(&sem);
return 0;
}
4. 读写锁(rwlock)
读写锁适合读多写少的场景,它区分读操作和写操作:多个线程可以同时持有读锁进行读操作,但是写锁是互斥的,同一时间只能有一个线程持有写锁,且持有写锁时不能有线程持有读锁。读写锁可以提高读多写少场景下的并发性能,避免读操作之间不必要的互斥。
下面是读写锁的使用示例:
#include <stdio.h>
#include <pthread.h>
// 定义共享数据和读写锁
int shared_data = 0;
pthread_rwlock_t rwlock;
// 读线程函数
void* read_func(void* arg) {
pthread_rwlock_rdlock(&rwlock);
printf("读线程 %ld 读取数据: %dn", (long)arg, shared_data);
pthread_rwlock_unlock(&rwlock);
return NULL;
}
// 写线程函数
void* write_func(void* arg) {
pthread_rwlock_wrlock(&rwlock);
shared_data++;
printf("写线程 %ld 写入数据: %dn", (long)arg, shared_data);
pthread_rwlock_unlock(&rwlock);
return NULL;
}
int main() {
pthread_t read_threads[3], write_threads[2];
// 初始化读写锁
pthread_rwlock_init(&rwlock, NULL);
// 创建读线程和写线程
for (int i = 0; i < 3; i++) {
pthread_create(&read_threads[i], NULL, read_func, (void*)(long)i);
}
for (int i = 0; i < 2; i++) {
pthread_create(&write_threads[i], NULL, write_func, (void*)(long)i);
}
// 等待所有线程结束
for (int i = 0; i < 3; i++) {
pthread_join(read_threads[i], NULL);
}
for (int i = 0; i < 2; i++) {
pthread_join(write_threads[i], NULL);
}
// 销毁读写锁
pthread_rwlock_destroy(&rwlock);
return 0;
}
不同同步方式的选择建议
在实际开发中可以根据场景选择合适的同步方式:
- 如果只是简单的共享资源互斥访问,优先选择互斥锁,实现简单开销小
- 如果需要线程之间等待某个条件成立,选择条件变量配合互斥锁
- 如果需要控制并发访问的线程数量,或者实现更复杂的同步逻辑,选择信号量
- 如果是读多写少的场景,选择读写锁可以提升并发性能
需要注意的是,使用这些同步方式时都要避免死锁问题,比如加锁后一定要记得解锁,不要嵌套获取多个锁时形成循环等待。
linux多线程线程同步mutexcondition_variablesemaphore修改时间:2026-07-16 17:57:40