互斥锁保护std :: condition_variable

时间:2016-07-04 12:40:28

标签: c++ multithreading c++11 mutex condition-variable

  

即使共享变量是原子的,也必须在   互斥为了正确地将修改发布到等待中   线。任何打算在std :: condition_variable上等待的线程都有   在与使用的互斥锁相同的互斥锁上获取std :: unique_lock   保护共享变量

     

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

据我所知,通过使用互斥锁保护std :: condition_variable,如果等待的线程实际上没有等待,我们可以防止丢失通知。这里已回答: Shared atomic variable is not properly published if it is not modified under mutex

我想知道的是,是否可以仅使用互斥锁来保护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;
}

2 个答案:

答案 0 :(得分:0)

必须设置互斥锁保护标志,并且当互斥锁仍然被保持时,条件变量会发出信号:

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

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

bool proceed;

就足够了。只有在持有关联的互斥锁时才能访问proceed,使proceed atomic完全没有任何结果。

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

答案 1 :(得分:0)

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

// 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);实际上正在等待时发生。

现在,您的(学术类)案件怎么样?

// 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)评估为true,则在调用cw.wait(l);之前不会发生通知

结论

我相信您的第一个示例还可以,并且cppreference中的引用不正确。