我有两个线程。一个线程充当计时器线程,需要在固定的时间间隔内将通知发送到另一个线程。我打算使用C++条件变量。 (关于如何在following link中使用C++条件变量及其陷阱和陷阱,有一篇不错的文章)
我有以下限制/条件:
使用上面的链接作为指导,我整理了以下代码
// conditionVariableAtomic.cpp
#include <atomic>
#include <condition_variable>
#include <iostream>
#include <thread>
#include <iostream> // std::cout, std::endl
#include <thread> // std::this_thread::sleep_for
#include <chrono> // std::chrono::seconds
std::mutex mutex_;
std::condition_variable condVar;
std::atomic<bool> dataReady{false};
void waitingForWork(){
int i = 0;
while (i++ < 10)
{
std::cout << "Waiting " << std::endl;
{
std::unique_lock<std::mutex> lck(mutex_);
condVar.wait(lck, []{ return dataReady.load(); }); // (1)
dataReady = false;
}
std::cout << "Running " << std::endl;
// Do useful work but no critical section.
}
}
void setDataReady(){
int i = 0;
while (i++ < 10)
{
std::this_thread::sleep_for (std::chrono::seconds(1));
dataReady = true;
std::cout << "Data prepared" << std::endl;
condVar.notify_one();
}
}
int main(){
std::cout << std::endl;
std::thread t1(waitingForWork);
std::thread t2(setDataReady);
t1.join();
t2.join();
std::cout << std::endl;
}
我使用原子谓词来避免虚假唤醒,但不要在通知线程中使用lock_guard
。我的问题是:
std::unique_lock<std::mutex> lck(mutex_);
。但是我限制了std::unique_lock<std::mutex> lck(mutex_);
的范围,即将以下代码部分std::unique_lock<std::mutex> lck(mutex_);
condVar.wait(lck, []{ return dataReady.load(); }); // (1)
dataReady = false;
在范围块(也称为{ .... }
)内,这样互斥量在等待条件结束后就被解锁(接收器随后进行了一些有用的工作,但是由于没有关键部分,因此不需要在整个时间内都保持互斥量循环)。在这种情况下,这种有限的范围界定是否还会产生后果/副作用?还是需要在整个while循环中锁定unique_lock<std::mutex>
? 最佳答案
您的代码具有竞争条件。在检查dataReady
谓词中的wait
值和实际开始等待之间,另一个线程可以设置dataReady
并调用notify_one
。在您的示例中,这并不重要,因为您只会错过一个通知,然后在下一个通知中唤醒一秒钟。
另一个竞争条件是,您可以在一个线程中将dataReady
设置为true
,在另一个线程中将dataReady
设置回false
,然后在第一个线程中调用notify_one
,这又将导致等待时间比预期的更长。
设置dataReady
并使用condition变量以避免这些竞争时,应该在两个线程中都持有互斥体。
您可以通过使用原子计数器而不是 bool(boolean) 值来避免第二种竞争条件,在一个线程上将其递增,然后在另一个线程上递减,然后在谓词中检查其是否为非零。
关于c++ - 将C++条件变量与原子谓词一起使用,但不使用互斥,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63986619/