pthread_cond_wait内部逻辑

简单介绍

        引入 pthread_cond_wait 函数的概念,它是 POSIX 线程库中用于条件变量等待的函数。一下是此函数运行时内部逻辑:

1. pthread_cond_wait 函数的调用过程

        描述当程序调用 pthread_cond_wait 函数时会发生什么。包括将执行流放入 PCB 等待队列、解锁等待。

2. 等待被唤醒

        解释在等待被唤醒期间会发生的事情,以及当条件变量满足时,如何唤醒等待线程。

3. 从等待队列中移除

        说明当线程被唤醒后,操作系统如何从 PCB 等待队列中移除线程。

4. 抢占互斥锁

        详细介绍线程在等待结束后,如何尝试抢占互斥锁。

5. 抢占互斥锁的阻塞逻辑

        解释当线程无法立即获得互斥锁时,会发生什么。包括线程被切换出 CPU、上下文信息的保存等。

6. 重新抢占互斥锁

        描述当线程再次获得 CPU 资源后,如何继续抢占互斥锁。

7. 等待结束

        说明当线程最终获得互斥锁时,pthread_cond_wait 函数如何返回。

详解

        条件变量的等待

int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict conpthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);

        为什么这两个接口中有互斥锁?

        条件不会无缘无故地突然变得满足了, 必然会牵扯到共享数据的变化。所以一定要有互斥锁来保护。没有互斥锁, 就无法安全地获取和修改共享数据。

        同步并没有保证互斥,而保证互斥是使用到了互斥锁。

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

        上面的解锁和等待不是原子操作。解锁以后, 调用cond_wait之前,如果已经有其他线程获取到了互斥量, 并且满足了条件, 同时发出了通知信号, 那么cond_wait将错过这个信号, 可能会导致线程永远处于阻塞状态。所以解锁加等待必须是一个原子性的操作, 以确保已经注册到事件的等待队列之前, 不会有其他线程可以获得互斥量。

        那先注册等待事件, 后释放锁不行吗?注意, 条件等待是个阻塞型的接口, 不单单是注册在事件的等待队列上, 线程也会因此阻塞于此, 从而导致互斥量无法释放, 其他线程获取不到互斥量, 也就无法通过改变共享数据使等待的条件得到满足, 因此这就造成了死锁。

pthread_mutex_lock(&m);
while(condition_is_false)
	pthread_cond_wait(&v,&m);//此处会阻塞
/*如果代码运行到此处, 则表示我们等待的条件已经满足了,
*并且在此持有了互斥量
*/
/*在满足条件的情况下, 做你想做的事情。
*/
pthread_mutex_unlock(&m);

        pthread_cond_wait函数只能由拥有互斥量的线程来调用, 当该函数返回的时候, 系统会确保该线程再次持有互斥量, 所以这个接口容易给人一种误解, 就是该线程一直在持有互斥量。事实上并不是这样的。这个接口向系统声明了我在PCB等待序列中之后, 就把互斥量给释放了。这样其他线程就有机会持有互斥量,操作共享数据, 触发变化, 使线程等待的条件得到满足。

pthread_cond_wait内部会进行解锁逻辑,则一定要先放到PCB等待序列中,再进行解锁。
while(condition_is_false)
	pthread_cond_wait(&v,&m);//此处会阻塞
if(condition_is_false)
	pthread_cond_wait(&v,&m);//此处会阻塞

        唤醒以后, 再次检查条件是否满足, 是不是多此一举?

        因为唤醒中存在虚假唤醒(spurious wakeup) , 换言之,条件尚未满足, pthread_cond_wait就返了。在一些实现中, 即使没有其他线程向条件变量发送信号, 等待此条件变量的线程也有可能会醒来。

        条件满足了发送信号, 但等到调用pthread_cond_wait的线程得到CPU资源时, 条件又再次不满足了。好在无论是哪种情况, 醒来之后再次测试条件是否满足就可以解决虚假等待的问题。

        pthread_cond_wait内部实现逻辑:

  1. 将调用pthread_cond_wait函数的执行流放入到PCB等待队列当中

  2. 解锁

  3. 等待被唤醒

  4. 被唤醒之后:

1、从PCB等待队列中移除出来

2、抢占互斥锁

情况1:拿到互斥锁,pthread_cond_wait就返回了

情况2:没有拿到互斥锁,阻塞在pthread_cond_wait内部抢锁的逻辑中

当阻塞在pthread_cond_wait函数抢锁逻辑中时,一旦执行流时间耗尽,意味着线程就被切换出来了,程序计数器就保存的是抢锁的指令,上下文信息保存的就是寄存器的值

当再次拥有CPU资源后,恢复抢锁逻辑

直到抢锁成功,pthread_cond_wait函数才会返回

  • 29
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值