Condition variable 是怎么告诉调度器这个线程不应该被调度,又是怎样唤醒的呢?

2020-09-08 20:39:44 +08:00
 dangyuluo

最近工作中发现了一个 Bug,就是有一个 condition variable 在临界区内调用了notify_one,让另一个在cv.wait的线程唤醒了一下然后立马又被 blocked 了。虽然不影响逻辑,但是会带来额外的线程切换负担。

公司的大神说: You shouldn't notify_one() while holding the mutex.

虽然问题解决了(通过lock.unlock(); cv.notify_one(); lock.lock();),但是这引起了我好奇,cv.wait是如何告诉调度器本线程不应该被调度的?又是怎样在得到信号后唤醒线程的?

请高人帮忙解答一下吧,谢谢!

1759 次点击
所在节点    C++
3 条回复
ysc3839
2020-09-08 21:03:04 +08:00
Windows 下 cv.wait 估计是调用 SleepConditionVariableSRW,可以参考一下 ReactOS 的实现 https://doxygen.reactos.org/da/d99/condvar_8c_source.html
RtlSleepConditionVariableSRW 是直接调用 InternalSleep 的,而 InternalSleep 则是通过调用 NtWaitForKeyedEvent 来等待的,结论是用了内核提供的机制。

Linux 下 cv.wait 估计是调用 pthread_cond_wait,可以参考各类 pthread 的实现,为了代码更加简单,这里选择参考 musl (下面的 GitHub 仓库是一个非官方的 musl mirror,用 GitHub 只是为了方便搜索)
pthread_cond_wait 是直接调用 pthread_cond_timedwait 的 https://github.com/ifduyue/musl/blob/0b0640219338b80cf47026d1970b5503414ed7f3/src/thread/pthread_cond_wait.c
pthread_cond_timedwait 是通过 __timedwait_cp 来等待的 https://github.com/ifduyue/musl/blob/0b0640219338b80cf47026d1970b5503414ed7f3/src/thread/pthread_cond_timedwait.c#L100
而 __timedwait_cp 则是通过 futex syscall 来等待的 https://github.com/ifduyue/musl/blob/0b0640219338b80cf47026d1970b5503414ed7f3/src/thread/__timedwait.c#L52
结论也是用了内核提供的机制。
dangyuluo
2020-09-08 21:22:00 +08:00
@ysc3839 谢谢 非常有帮助
codehz
2020-09-09 16:05:52 +08:00
建议直接看正确用法,具体实现是有可能发生变化的(所以不能依赖底层的行为
https://zh.cppreference.com/w/cpp/thread/condition_variable

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/705309

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX