返回信息流rt。
最近在看一个线程池的库,里面all()前加锁了,one()前面没加。这怎么区别对待?
我查到是不加锁notify的话,可能线程a执行wait(),但还没有进入阻塞状态释放锁,那这个线程就没有被通知到,被遗漏掉了
这是一条镜像帖。来源:北邮人论坛 / cpp / #102541同步于 2023/4/3
该镜像源已超过 30 天没有更新,可能在源站已被删除。
CPP机器人发帖
多线程条件变量notify_one(),notify_all()前要加锁吗
h0714
2023/4/3镜像同步6 回复
订阅后,新回复会通过你的通知中心匿名送达。
6 条回复
bing免费版的回答:
> 多线程条件变量notify_one(),notify_all()前要加锁吗?
您好,这是必应。根据我的搜索结果,多线程条件变量notify_one ()和notify_all ()的区别如下:
notify_one ()只唤醒等待队列中的一个线程,不存在锁争用,所以能够立即获得锁。其余的线程不会被唤醒,需要等待再次调用notify_one ()或者notify_all ()123。
notify_all ()唤醒所有等待队列中的线程,存在锁争用,只有一个线程能够获得锁。其余的线程会继续尝试获得锁,而不会再次阻塞123。
通知线程不需要持有和等待线程相同的互斥锁,事实上这样做是一种低效的做法,因为被通知的线程会立即再次阻塞,等待通知线程释放锁4。但是在一些需要精确控制事件顺序的情况下,通知时加锁可能是必要的,例如如果等待线程在条件满足时退出程序,导致通知线程的条件变量被销毁。这时如果在解锁和通知之间发生了虚假唤醒,就会导致在已经销毁的对象上调用通知4。
Learn more:
1. blog.csdn.net
2. blog.csdn.net
3. blog.csdn.net
4. en.cppreference.com
随手一搜第一条就是正确答案的问题都要上论坛来问了吗?
The notifying thread does not need to hold the lock on the same mutex as the one held by the waiting thread(s).
https://en.cppreference.com/w/cpp/thread/condition_variable/notify_all
C++的API允许你不加锁就notify,但是我见到的不加锁就notify的用例基本上都有问题。
cppreference.com上有这么一句(https://en.cppreference.com/w/cpp/thread/condition_variable/notify_all):
The notifying thread does not need to hold the lock on the same mutex as the one held by the waiting thread(s). Doing so may be a pessimization, since the notified thread would immediately block again, waiting for the notifying thread to release the lock, though some implementations recognize the pattern and do not attempt to wake up the thread that is notified under lock.
【 在 h0714 的大作中提到: 】
: rt。
: 最近在看一个线程池的库,里面all()前加锁了,one()前面没加。这怎么区别对待?
: 我查到是不加锁notify的话,可能线程a执行wait(),但还没有进入阻塞状态释放锁,那这个线程就没有被通知到,被遗漏掉了
我在平时使用时,notify时本身不需要加锁,真正加锁的是那个需要控制的条件。需要保证无论线程怎么切,你的逻辑是正确的。基本上常见的有以下几种顺序:
线程1:加锁,while判断条件,不满足则wait
线程2:加锁,改变条件,释放锁,锁外notify
顺序1:
线程2锁内代码先执行,线程1锁内代码再执行,此时while条件不满足,不会wait,所以线程1的notify具体在线程2锁内代码逻辑前还是逻辑后执行不重要,反正也没有用。
顺序2:线程1锁内代码先执行,条件不满足,阻塞在wait,线程2锁内代码再执行,改变条件,解锁,notify,线程1内wait被唤醒。
主要核心要考虑的就是一定要让线程1和线程2的判断条件那的代码互斥,无论线程怎么切不能有问题。
最经典的没有互斥好导致的问题顺序就是:
一个线程判断了条件,满足;此时切线程,notify;此时又切回原线程,wait,彻底卡死了。
linux下wait操作可以理解为把自己挂在一个待唤醒队列上,而notify可以理解为在唤醒队列上找一个或全部去唤醒。而先notify后wait的后果就是,先找唤醒队列,空,notify操作结束;wait操作把自己挂唤醒队列上,好了,此时没人唤醒了,除非等待下一个notify信号。如果是优雅退出操作,很可能后面没有notify信号了
再补充一下吧,就用我上文说的那个最经典的互斥有问题的那个场景来分析一下我给出的那个线程1和线程2的例子为什么没有问题。
就按照经典场景的顺序来,那就是线程1先执行,加锁,while判断条件满足,此时(在wait前),切到线程2,此时我们再分2种情况讨论一下:
情况1:线程2锁内代码未执行,此时要执行,抢锁,没抢到,无奈只能等线程1自己把锁内代码执行完主动释放锁后,线程2才能抢到锁并执行锁内代码,此时就是顺序2的逻辑。
情况2:线程2的锁内代码已经执行完了,那么是什么时候执行的呢?现在线程1持有锁,那必然就是线程1抢到锁前执行完并释放的锁,那整体就是顺序1的逻辑(即,实际上,是线程2的锁内代码先执行的)
由此可见,通过锁控制互斥区,可以避免经典错误的复现。
所以实际真正加锁的是条件变化与判断的逻辑,并不是notify本身。
可以理解为通过锁来做一个屏障,使得锁内代码是一个整体,不可分割。不能因为切线程导致逻辑出问题。
再提一句:楼主说的"查到的那句",本质上不是"执行完wait,但还没阻塞释放锁",而是"执行完条件判断逻辑,还没有执行wait"
【 在 flyfree 的大作中提到: 】
: 我在平时使用时,notify时本身不需要加锁,真正加锁的是那个需要控制的条件。需要保证无论线程怎么切,你的逻辑是正确的。基本上常见的有以下几种顺序:
: 线程1:加锁,while判断条件,不满足则wait
: 线程2:加锁,改变条件,释放锁,锁外notify
: ............