最近工作中发现了一个 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
是如何告诉调度器本线程不应该被调度的?又是怎样在得到信号后唤醒线程的?
请高人帮忙解答一下吧,谢谢!
1
ysc3839 2020-09-08 21:03:04 +08:00 1
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 结论也是用了内核提供的机制。 |
3
codehz 2020-09-09 16:05:52 +08:00
建议直接看正确用法,具体实现是有可能发生变化的(所以不能依赖底层的行为
https://zh.cppreference.com/w/cpp/thread/condition_variable |