繁体   English   中英

互斥保护 std::condition_variable

[英]Mutex protecting std::condition_variable

即使共享变量是原子的,也必须在互斥锁下进行修改,才能将修改正确发布到等待线程。 任何打算等待 std::condition_variable 的线程都必须在用于保护共享变量的同一互斥锁上获取 std::unique_lock

http://en.cppreference.com/w/cpp/thread/condition_variable

我明白,通过使用互斥锁保护 std::condition_variable,我们可以防止在等待线程实际上没有等待时丢失通知。 这里已经回答: 如果没有在互斥锁下修改共享原子变量,则不会正确发布

我想知道的是,是否可以仅使用互斥锁来保护 std::condition_variable 以及对共享数据的某种其他形式的保护? 如果我们修改另一个答案中给出的示例,这会起作用吗?

std::atomic_bool proceed(false);
std::mutex m;
std::condition_variable cv;

std::thread t([&m,&cv,&proceed]()
{
    {
        std::unique_lock<std::mutex> l(m);
        while(!proceed) {
            hardWork();
            cv.wait(l);
        }
    }
});

proceed = true;

{
    std::lock_guard<std::mutex> lock(m);
}
cv.notify_one();
t.join();

或者我错过了内存排序或缓存有什么问题吗?


更新


我知道互斥锁通常也保护共享数据,使用原子变量只是一个例子。 问题不在于如何保护共享数据,而在于是否有必要使用相同的互斥锁来保护两者。 另一个使用第二个互斥锁的例子:

bool proceed(false);
std::mutex boolMutex;

std::mutex cvMutex;
std::condition_variable cv;
std::unique_lock<std::mutex> l(cvMutex);

void setBool()
{
    std::lock_guard<std::mutex> lock(boolMutex);
    proceed = true;
}

bool checkBool()
{
    std::lock_guard<std::mutex> lock(boolMutex);
    return proceed;
}

void worker()
{
    while (true)
    {
        cv.wait(l);
        if (checkBool()) {
            // Do work
            return;
        }
    }
}

int main()
{
    std::thread t(worker);
    setBool();

    {
        std::lock_guard<std::mutex> lock(cvMutex);
    }
    cv.notify_one();
    t.join();

    return 0;
}

必须设置 mutex-protected 标志,并通知条件变量,而 mutex 仍被保留:

{
    std::lock_guard<std::mutex> lock(m);
    proceed = true;
    cv.notify_one();
}

此外,在这种情况下, proceed标志不需要是原子实体。 一个简单的

bool proceed;

就足够了。 只有在持有相关联的互斥锁时才能访问proceed ,因此使proceed原子化绝对不会完成任何事情。

atomic实体用于处理不涉及任何互斥锁的奇异并发情况。

我不认为山姆的回答是正确的。 考虑以下代码:

// thread #1:
std::unique_lock<std::mutex> l(m);
while (!proceed) cv.wait(l);

// thread #2:
proceed = true; // atomic to avoid data race
cv.notify_one();

这里的问题是以下可能的事件序列:

thread #1: while (!proceed) // evaluated as true
thread #2: proceed = true;
thread #2: cv.notify_one();
thread #1: cv.wait(l); // never gets notified

为了避免这种情况,一个典型的解决方案是使用相同的互斥锁保护proceed修改:

// thread #1:
std::unique_lock<std::mutex> l(m);
while (!proceed) cv.wait(l);

// thread #2:
{
   std::lock_guard<std::mutex> l(m);
   proceed = true; // does not need to be atomic
}
cv.notify_one();

现在, proceed = true; 必须发生在while (!proceed)或之后cv.wait(l); 开始等待; 都可以。 在第一种情况下,根本不需要等待; 在第二种情况下, cv.notify_one(); 保证仅在cv.wait(l);时发生cv.wait(l); 实际上是在等待。

现在,你的(学术类)案例呢?

// thread #1:
std::unique_lock<std::mutex> l(m);
while (!proceed) cv.wait(l);

// thread #2:
proceed = true; // atomic to avoid data race
{
   std::lock_guard<std::mutex> lock(m);
}
cv.notify_one();

我相信这种情况也是完全有效的,因为上述错误情况也不会发生。 原因很简单。 如果while (!proceed)被评估为 false,同样,没有等待。 并且,如果while (!proceed)被评估为真,则通知不会发生,直到cw.wait(l); 被调用

结论

我相信你的第一个例子没问题,cppreference 的引用是不正确的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM