我已经实现了一个类,它允许我将线程与条件变量同步。我发现关于 notify_all 应该在锁内还是在锁外完成的冲突信息。我发现了两种方式构建的例子。
首先释放锁的理由是为了防止等待线程在被通知释放后立即阻塞在互斥锁上。
反对首先释放锁的论点是等待线程可能会错过通知。
两个版本的release函数在这里:
// version 1 - unlock then notify.
void release(int address = 1)
{
{
std::lock_guard<std::mutex> lk(_address_mutex);
_address = address;
}
_cv.notify_all();
}
// version 2 - notify then unlock
void release(int address = 1)
{
std::lock_guard<std::mutex> lk(_address_mutex);
_address = address;
_cv.notify_all();
}
作为参考,等待代码如下所示:
bool wait(const std::chrono::microseconds dur, int address = 1)
{
std::unique_lock<std::mutex> lk(_address_mutex);
if (_cv.wait_for(lk, dur, [&] {return _address == address; }))
{
_address = 0;
return true;
}
return false;
}
在版本 1 中,允许互斥锁在 notification_all 之前超出范围,等待线程是否存在丢失通知的风险?如果是这样,它是如何发生的? (我不清楚这如何导致错过通知。)
我可以清楚地看到在通知期间保持互斥体锁定如何导致等待线程立即进入等待状态。但如果它能防止错过通知,这只是一个很小的代价。
没有释放锁的风险if互斥锁在条件测试状态更改和 thr 通知之间的某个时间间隔内保持。
{
std::lock_guard<std::mutex> lk(_address_mutex);
_address = address;
}
_cv.notify_all();
在这里,互斥体在之后被解锁_address
被改变了。所以没有风险。
如果我们修改_address
为了成为原子的,天真地这看起来是正确的:
{
std::lock_guard<std::mutex> lk(_address_mutex);
}
_address = address;
_cv.notify_all();
但事实并非如此;在这里,互斥量在修改条件测试和通知之间的整个期间被释放,
_address = address;
{
std::lock_guard<std::mutex> lk(_address_mutex);
}
_cv.notify_all();
然而,上面的内容又变得正确了(虽然有点奇怪)。
风险在于条件测试将在互斥体处于活动状态(为假)的情况下进行评估,然后进行更改,然后发送通知,then等待线程等待通知并释放互斥体。
waiting|signalling
lock
test
test changed
notification
listen+unlock
以上是错过通知的示例。
只要我们在测试更改之后和通知之前将互斥体保存在任何地方,就不会发生这种情况。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)