锁定的互斥锁是否保护condition_variable和数据?

Does a locked mutex protect the condition_variable as well as the data?

本文关键字:variable 数据 condition 保护 是否 锁定      更新时间:2023-10-16

考虑以下 this_thread::sleep_for(( 函数的实现。它来自斯特劳斯特鲁普的书,"CPL",4,第1232页。我通过 (i( 重命名它和 (ii( 将函数与客户端代码分离来修改它:

#include <chrono>       /// milliseconds
#include <mutex>        /// mutex, unique_lock
#include <condition_variable>   /// condition_variable
/// declarations ...
namespace ext
{
void sleep_for(int ms);
}

/// implementation ...
void ext::sleep_for(int ms)
{
std::mutex mtx;
std::condition_variable timercv;
/// acquire mtx
std::unique_lock<std::mutex> lck {mtx};
/// release and reacquire mutex
timercv.wait_for(lck, std::chrono::milliseconds {ms});
}  // implicitly release mtx


斯特劳斯特鲁普说:

互斥锁保护wait_for((免受数据争用的影响。wait_for(( 在进入睡眠状态时释放其互斥锁,并在线程解锁时重新获取互斥锁。


我想问:
1(通过说mutex保护wait_for()免受数据竞争,我认为Stroustrup指的是condition_variable本身,对吧?

2( 为什么condition_variable需要并发访问保护?它是一个局部变量,而不是全局变量。线程对ext::sleep_for()函数的每次调用都将具有condition_variable的单独副本。

3(在此程序中,没有要保护的全局数据(例如就绪标志等(mutex能保护condition_variable吗?如果是这样,从什么?正如我所说,调用此函数的每个线程都将访问一个单独的副本。

-- 编辑 --
这是我对为什么在此代码中使用mutexcondition_variable的解释:
1(需要mutex才能获得对它的锁定。
2(condition_variable释放互斥锁上的锁,进入睡眠状态指定的时间段,然后重新获取锁。

代码中存在错误。允许条件变量虚假唤醒;您有责任处理该情况。 上面的代码中没有任何内容可以处理它。 这里有一个简单的解决方法:

timercv.wait_for(lck, std::chrono::milliseconds {ms}, []{return false;});

现在,任何早起都将被拒绝。

这仍然是条件变量的一个极其奇怪的用法。 通常它们用于在一个或多个线程之间进行通信。 在这种情况下,互斥锁和条件变量以及条件变量检查的事物形成三元组。

Stroustrup似乎在谈论一个更典型的案例。

在这种更典型的情况下,互斥锁保护对条件变量检查的事物的访问,并且必须锁定才能对条件变量执行某些操作。 由于这些是低级线程原语,您应该遵循有效的标准模式,或者您必须确切地了解每个操作的作用以及它提供的保证并证明您的代码是正确的。 任何简化都将以"告诉孩子谎言"的形式出现,并且基于谎言设计新的使用模式将没有任何可靠性。

确切的规格在标准中描述,这些是您应该参考的规格。 请注意,规格在标准修订版之间发生了变化。

简而言之,您应该找到经过验证的使用模式并使用它们,然后封装它们,永远不要碰它们。

编写睡眠的一个更简单的方法是:

void ext::sleep_for(int ms) {
std::mutex mtx;
std::unique_lock<std::mutex> lock1(mtx);
std::unique_lock<std::mutex> lock2(mtx, std::defer_lock_t{});
lock2.try_lock_for(std::chrono::milliseconds {ms});
}  // implicitly release mtx

它没有那么复杂的一组条件,并且使用更简单的基元。