考虑这种情况(这是cppreferences中的标准用法):
std::atomic<bool> condition(false);
thread 1:
lock
// check condition the last time before waiting
if (condition == true) { unlock; return; }
// to be clear, I expand "wait" here
{
// In my understanding
post task to waiting list
unlock
yield
lock
check condition
...
}
thread 2:
lock
condition=true
notify_one
unlock
我知道线程2需要
lock
和unlock
,这样在检查条件之后但在将任务发布到等待列表之前,notify_one
不会被调用(在我的理解中,如果任务没有执行,notify_one
不会执行任何操作)已发布到等候名单)。
但是,我认为修改条件(示例中的
condition=true
)不需要拥有互斥锁,也就是说,我认为这也应该有效:
thread 2:
condition=true
lock
notify_one
unlock
因为在我的理解中,如果在线程1锁定之前调用
notify_one
,那么wait之前的条件检查将返回true,线程1根本不会等待。如果在线程 1 解锁后调用 notify_one
,则任务已被发布到等待列表中,因此线程 1 可以唤醒并检查条件,该条件已经为 true。
但是,cppreferences表示即使共享变量是原子的,也必须在拥有互斥锁的同时对其进行修改。有什么我想念的吗?
相关:
但是我的问题比他们更具体。
更新:我尝试测试我在不拥有互斥锁的情况下修改条件的想法,到目前为止效果很好:
#include <iostream>
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <thread>
std::mutex mu;
std::atomic<bool> condition(false);
std::condition_variable cv;
void thread_1() {
std::unique_lock<std::mutex> lock(mu);
if (condition.load() == true)
return;
cv.wait(lock);
}
void thread_2() {
condition.store(true);
std::unique_lock<std::mutex> lock(mu);
cv.notify_one();
}
int main() {
for (size_t i = 0; i < 100000; ++i) {
std::thread t1(thread_1);
std::thread t2(thread_2);
t1.join();
t2.join();
}
return 0;
}
典型用法如下:
std::deque<int> shared_data;
bool consumer_waiting = false;
condition_variable cond;
mutex mut;
void produce(int data) {
bool consumer_was_waiting;
{
scoped_lock lock(mutex);
consumer_was_waiting = consumer_waiting;
if (consumer_was_waiting)
consumer_waiting = false;
shared_data.push_back(data);
}
if (consumer_was_waiting)
cond.notify_one(); // this call is outside the mutex scope
}
int consume() {
scoped_lock lock(mutex);
while (shared_data.empty()) {
consumer_waiting = true;
cond.wait(lock);
}
return shared_data.pop_front();
}