欢迎光临散文网 会员登陆 & 注册

std::condition_variable

2022-11-17 11:00 作者:xhy2023  | 我要投稿


三个要素:条件,条件变量,互斥锁。

  1. 条件:通常就是定义的一些变量,比如is_start, is_stop这些,和临界区域是绑定的。

  2. 条件变量:std::condition_variable condition;  用来阻塞线程和唤醒线程。

  3. 互斥锁:std::mutex mutex;  条件变量需要和一个互斥锁绑定,这个互斥锁的作用为:a. 互斥地访问临界资源。 b. 保护条件。保护临界资源互斥访问就是保护条件互斥访问,两者绑定的。

多线程访问一个共享资源(或称临界区),不仅需要用互斥锁实现独享访问避免并发错误,在获得互斥锁进入临界区后,有时还需检查特定条件是否成立。当某个线程修改测试条件后,将通知其它正在等待条件的线程继续往下执行。PV操作的一个代码实现:


wait线程从条件不满足,等待到重新执行过程:

  1. (wait前必须先加锁)调用线程将自己放入等待队列,mutex解锁。(调用线程己加入等待队列并解锁,此时,允许其他线程改变“测试条件”)

  2. 挂起,等待pthread_cond_signal或pthread_cond_broadcast去唤醒。(其他线程改变测试条件,当条件满足时会发出通知)

  3. 被唤醒,mutex加锁


为什么在wait之前需要加锁?

关闭了从检测“测试条件”的时刻到将线程放入到等待队列之间的这段“时间窗口”,使“测试条件”在线程加入等待队列之前不会被其他线程修改,确保调用线程不会错过“测试条件”的改变。


为什么使用while(1)语句来循环判断“测试条件”?

线程API存在一个事实(很多语言中都如此,不仅仅是C++),就是即使在没有通知条件变量的情况下线程也可能被唤醒,这样的唤醒称为虚假唤醒(spurious wakeups),但此时“测试条件”往往并没有被满足。因此正确的做法是,通过while循环确认等待的“测试条件”是否确己发生并将其作为唤醒后的首个动作来处理,一旦确认是“虚假唤醒”则继续wait等待。而如果仅使用if语句,则唤醒后无法进行这种确认从而可能导致错误。


pthread_cond_signal 和 pthread_mutex_unlock顺序问题

  • pthread_cond_signal放于pthread_mutex_unlock之前:wait线程被唤醒后是会对mutex重新加锁的,但此时锁可能还没有被notify线程释放(会发生这种现象就是因为系统对线程的调度),会造成等待线程从内核中唤醒然后又回到内核空间(因为cond_wait返回后会有原子加锁的行为),所以一来一回会有性能的问题。但在Linux中推荐这种模式。

  • pthread_cond_signal放于pthread_mutex_unlock之后:不会出现之前说的那个潜在的性能损耗,因为在signal之前就已经释放锁了。但如果unlock和signal之前,有个低优先级的线程正在mutex上等待的话,那么这个低优先级的线程就会抢占高优先级的线程(cond_wait的线程)。


std::condition_variable的评论 (共 条)

分享到微博请遵守国家法律