std::condition_variable_any 的 libc++ 实现

问题描述 投票:0回答:2

条件变量对于

notify()
unlock_sleep()
(在
wait()
中使用的虚构函数调用,其中互斥体被解锁并且线程作为一个原子操作序列休眠)操作应该具有单一顺序。为了使用任意可锁定来实现这一点
std::condition_variable_any
实现通常在内部使用另一个互斥锁(以确保原子性并继续休眠)

如果内部

unlock_sleep()
notify()
notify_one()
notify_all()
)操作相对于彼此而言不是原子的,那么您将面临一个线程解锁互斥体、另一个线程发信号、然后原始线程进入睡眠状态并且永远不会发生的风险。醒来了。

我正在阅读 std::condition_variable_any 的 libstdc++ 和 libc++ 实现,并注意到 libc++ 实现中的这段代码

{lock_guard<mutex> __lx(*__mut_);}
__cv_.notify_one();

内部互斥体被锁定,然后在信号操作之前立即解锁。这不会带来我上面描述的问题的风险吗?

libstdc++ 似乎已经做到了这一点

c++ multithreading c++11 thread-safety condition-variable
2个回答
1
投票

C++11 及更高版本的标准明确规定“

notify_one
notify_all
的执行应是原子的”。因此,从某种意义上说,我认为您是正确的,内部互斥体应该在调用过程中保持到平台的底层条件变量通知调用(例如
pthread_cond_signal()

但是,我不认为 libc++ 实现会导致错过通知,因为如果没有通知线程在锁上同步,则等待线程在调用

wait()
时会传递到
notify_one()
(或两个线程之间的其他同步) (或
notify_all()
)无论如何,都无法确保两个线程中的哪一个是“第一个”通知或等待。因此,如果在 libc++ 的当前实现中可能会错过通知,那么如果将 libc++ 更改为在对平台通知 API 的调用中保持内部锁定,也可能会错过通知。

所以我认为libc++可以调用“好像”规则来表示

notify_one()
/
notify_any()
的实现“足够原子”。


0
投票

我不太清楚你的问题,但我们传递给

wait
的函数的目的是检查是否满足所需的条件。如果不是,
wait
将解锁外部锁并继续等待,直到发生(可能是虚假的)唤醒。

根据 libc++ 中的实现,如 C++ 的当前工作草案中所述(重点是我的):

notify_one
notify_all
的执行是原子的。
wait
wait_for
wait_until
的执行是在三个原子部分中执行的:

  1. 释放互斥体并进入等待状态。
  2. 等待的畅通;和
  3. 重新获取锁。

该实现的行为就好像

notify_one
notify_all
的所有执行以及
wait
wait_for
wait_until
执行的每个部分都以单个 未指定 总顺序执行,与“发生”一致之前”订单。

wait
的第一部分(解锁和等待)的原子性是由
condition_variable_any
的内部锁保证的。在
notify_one
notify_all
中,我们必须首先获取内部锁,以确保解锁-等待组合正确执行。
condition_variable_any
的其他部分已经是原子的,这要归功于内部的
condition_variable
,这有助于维持这种原子性,使得
notify_one
notify_all
中的内部锁能够立即释放。

© www.soinside.com 2019 - 2024. All rights reserved.