【Linux】线程同步

在介绍线程同步之前,我们需要先了解一下死锁的相关概念。
死锁:在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所占有用不会释放的资源而处于一种永久等待的状态。
无论线程还是进程都可能出现死锁

死锁的四个必要条件
  • 互斥:每个资源每次只能被一个执行流使用
  • 请求与保持条件:一个执行流因请求资源而阻塞时,对已获得的资源保持不放
  • 不剥夺条件:一个执行流已获得的资源在未使用完之前不能被强行剥夺
  • 循环等待条件:若干执行流之间形成一种头尾相接的循环等待资源的关系
避免死锁的方法
  • 破坏死锁的四个必要条件中的2,3,4条
  • 加锁的顺序保持一致即同步性
  • 避免未释放锁的场景
  • 资源一次性分配
线程同步

上一篇我们讲了Linux线程的互斥,利用锁的机制保证了线程的安全。本篇博客我们将为大家讲解Linux下的同步机制,因为时序问题,而导致程序异常,我们称之为竞争状态。而为了避免发生竞争状态,我们需要在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源称为同步
在OS中,我们提供互斥机制是为了保证数据安全性,同步机制为了保证合理性。

想要保证线程同步的合理性和高效性,我们就需要用到条件变量。当一个线程互斥地访问某个变量,它可能发现在其它线程改变状态之前,它什么也做不了。例如一个线程访问队列时,发现队列为空,它只能等待,直到其它线程将一个节点添加到队列中。这种情况就需要用到条件变量,下面我们来看看条件变量相关的函数,这些函数真的非常像互斥锁。

  1. 初始化条件变量:一般定义全局变量并初始化

int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
//参数一:要初始化的条件变量 参数二:一般设置为NULL

  1. 销毁

int pthread_cond_destroy(pthread_cond_t *cond)

  1. 在条件队列中等待条件满足

int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
//参数一为要等待的条件变量,参数二为一把互斥锁,这把锁的用途后面讲解

  1. 唤醒等待

int pthread_cond_broadcast(pthread_cond_t *cond);//唤醒所有的条件变量,惊群问题
int pthread_cond_signal(pthread_cond_t *cond); //唤醒某一个线程

使用条件变量例子:

此程序r2中的signal函数每隔1秒唤醒一次r1函数,所以每秒打印一次活动。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
pthread_cond_t cond;
pthread_mutex_t mutex;
void *r1( void *arg )
{
	while ( 1 ){
	pthread_cond_wait(&cond, &mutex);
	printf("活动\n");
	}
} 
void *r2(void *arg )
{
	while ( 1 ) {
	pthread_cond_signal(&cond);
	sleep(1);
	}
}
 int main( void )
{
	pthread_t t1, t2;
	pthread_cond_init(&cond, NULL);
	pthread_mutex_init(&mutex, NULL);
	
	pthread_create(&t1, NULL, r1, NULL);
	pthread_create(&t2, NULL, r2, NULL);
	pthread_join(t1, NULL);
	pthread_join(t2, NULL);
	
	pthread_mutex_destroy(&mutex);
	pthread_cond_destroy(&cond);
} 

在这里插入图片描述

为什么pthread_cond_wait函数参数需要传互斥锁:
pthread_mutex_lock(&mutex);
//进入临界区访问临界资源
while (condition_is_false) {
	pthread_cond_wait(&cond);
}
 pthread_mutex_unlock(&mutex);

  • 从上面代码中我们发现,当一个线程(可能是读线程)进入临界区时发现此时临界资源不具备读的条件,所以此时就需要另外一个线程对临界资源进行写操作,但是由于我们现在的读线程手中拿着锁,所以写线程拿不到锁,也就无法进入临界区。为了解决这一问题pthread_cond_wait函数在进行等待时会将锁自动释放,然后挂起。 值得注意的是,释放锁和挂起等待这俩步操作合成了一个原子操作。当满足条件被唤醒时,此时读线程同时又获得了互斥锁。

  • 那么释放锁和等待能不能不是原子的操作呢?
    大家先看下面的代码:

pthread_mutex_lock(&mutex);
while (condition_is_false) {
	pthread_mutex_unlock(&mutex);
	//解锁之后,等待之前,条件可能已经满足,信号已经发出,但是该信号可能被错过
	pthread_cond_wait(&cond);
	pthread_mutex_lock(&mutex);
} 
pthread_mutex_unlock(&mutex);

由于解锁和等待不是原子操作。调用解锁之后,pthread_ cond_ wait之前,如果已经有其他线程获取到互斥量,摒弃条件满足,发送了信号,那么pthread_ cond_ wait将错过这个信号,可能会导致线程永远阻塞在这个pthread_ cond_ wait。所以解锁和等待必须是一个原子操作。

条件变量使用规范
  1. 等待条件即线程等待
pthread_mutex_lock(&mutex);
while(条件为假)
	pthread_cond_wait(cond,&mutex);
	修改条件
	pthread_mutex_unlock(&mutex);
	
  1. 给条件发送信号即唤醒
pthread_mutex_lock(&mutex);
设置条件为真
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);

下篇博客我们就要使用条件变量来实现经典的生产者消费者模型。

<think>好的,我现在需要回答用户关于Linux环境下线程同步的方法和机制的问题。首先,我得回忆一下Linux提供的线程同步机制有哪些。根据之前提供的引用内容,用户提到的引用[1]和[4]都提到了条件变量,而引用[2]和[3]还提到了互斥锁、信号量、读写锁等。需要把这些机制都涵盖进去。 首先,我应该列出主要的同步方法,比如互斥锁、条件变量、信号量、读写锁和屏障。然后,每个方法需要给出简要的解释,并附上示例代码,这样用户能更直观地理解。同时,根据用户的要求,行内数学公式用$...$,独立公式用$$...$$,但这里可能数学公式不多,主要是代码示例。需要确保代码语法正确,使用中文回答,并在适当的地方添加引用标识。 接下来,我需要检查每个同步机制的正确用法。例如,互斥锁使用pthread_mutex_t,条件变量配合互斥锁使用,信号量有POSIX和System V两种,但通常推荐POSIX信号量。读写锁用pthread_rwlock_t,屏障用pthread_barrier_t。每个示例代码的结构应该正确,比如初始化、加锁解锁、销毁等步骤。 然后,要注意引用对应的来源。比如,互斥锁和条件变量在引用[1]、[2]、[3]、[4]中都有提及,需要合理分配引用标记。例如,条件变量在引用[1]和[3]中都有提到,所以可能在解释条件变量时加上[^1][^3]。 可能遇到的问题包括如何简明扼要地解释每个机制,同时保持示例代码的正确性。例如,信号量的示例可能需要展示sem_wait和sem_post的使用,而屏障则需要展示pthread_barrier_wait的用法。此外,还需要注意线程安全函数的提及,如引用[2]中的可重入函数,但用户的问题主要关注同步机制,所以可能不需要深入讨论线程安全函数。 最后,生成的相关问题需要围绕线程同步的方法展开,比如性能比较、死锁避免、适用场景等。确保问题与用户的问题相关,并且能够引导进一步的学习。 总结步骤: 1. 列出Linux下的线程同步机制:互斥锁、条件变量、信号量、读写锁、屏障。 2. 对每个机制进行简要说明,给出代码示例。 3. 添加对应的引用标记。 4. 生成相关问题。</think>### Linux 环境下线程同步的方法与示例 Linux 提供了多种线程同步机制,用于解决多线程并发访问共享资源时的互斥和协作问题。以下是常见方法及其实现示例: --- #### 1. **互斥锁(Mutex)** 通过锁机制保证同一时间只有一个线程访问共享资源。 **示例代码**: ```c #include <pthread.h> pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void* thread_func(void* arg) { pthread_mutex_lock(&mutex); // 加锁 // 访问共享资源 pthread_mutex_unlock(&mutex); // 解锁 return NULL; } ``` **特点**:简单高效,但可能引发死锁[^2][^4]。 --- #### 2. **条件变量(Condition Variable)** 用于线程间通信,当某个条件满足时唤醒等待的线程。需与互斥锁配合使用。 **示例代码**: ```c #include <pthread.h> pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; bool flag = false; void* consumer(void* arg) { pthread_mutex_lock(&mutex); while (!flag) { pthread_cond_wait(&cond, &mutex); // 等待条件成立 } // 条件满足后执行操作 pthread_mutex_unlock(&mutex); return NULL; } void* producer(void* arg) { pthread_mutex_lock(&mutex); flag = true; pthread_cond_signal(&cond); // 通知等待线程 pthread_mutex_unlock(&mutex); return NULL; } ``` **应用场景**:生产者-消费者模型。 --- #### 3. **信号量(Semaphore)** 通过计数器控制对共享资源的访问,支持多线程同时访问(但数量受限)。 **示例代码**: ```c #include <semaphore.h> sem_t sem; void* thread_func(void* arg) { sem_wait(&sem); // P操作(计数器减1) // 访问共享资源 sem_post(&sem); // V操作(计数器加1) return NULL; } // 初始化信号量(初始值为2) sem_init(&sem, 0, 2); ``` **特点**:适用于限流场景,如连接池管理。 --- #### 4. **读写锁(Read-Write Lock)** 允许多个读线程同时访问,但写线程独占资源。 **示例代码**: ```c #include <pthread.h> pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; void* reader(void* arg) { pthread_rwlock_rdlock(&rwlock); // 读锁 // 读取共享资源 pthread_rwlock_unlock(&rwlock); return NULL; } void* writer(void* arg) { pthread_rwlock_wrlock(&rwlock); // 写锁 // 修改共享资源 pthread_rwlock_unlock(&rwlock); return NULL; } ``` **适用场景**:读多写少的场景(如缓存系统)。 --- #### 5. **屏障(Barrier)** 同步多个线程,直到所有线程到达某个点后才继续执行。 **示例代码**: ```c #include <pthread.h> pthread_barrier_t barrier; void* thread_func(void* arg) { // 执行部分任务 pthread_barrier_wait(&barrier); // 等待其他线程 // 继续后续任务 return NULL; } // 初始化屏障(等待3个线程) pthread_barrier_init(&barrier, NULL, 3); ``` **应用场景**:并行计算中多阶段任务同步[^4]。 --- ### 对比与选择 | 机制 | 适用场景 | 复杂度 | |--------------|----------------------------|--------| | 互斥锁 | 简单互斥访问 | 低 | | 条件变量 | 线程间条件协作 | 中 | | 信号量 | 限流或资源池管理 | 中 | | 读写锁 | 读多写少场景 | 中 | | 屏障 | 多线程分阶段同步 | 高 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值