将std :: atomic <bool>与std :: mutex结合使用的正确性

时间:2017-06-07 08:09:19

标签: c++ multithreading c++11 stdatomic stdmutex

我写了一个共享优先级队列类 要发出停止提供数据的信号,我使用方法Cancel(),它将符号done设置为false,并且不允许应用程序写入/读取队列中的任何数据。我不确定将std::atomic<bool>std::mutexstd::condition_variable结合使用。我不确定,如果我的解决方案是线程安全的,或者竞争条件可能发生:

Enqueue方法的原始版本是:

std::deque<T> deque;
std::mutex mtx;
std::condition_variable cv;
std::atomic<bool> done;

SharedPriorityQueue() : done(false)
{
}

~SharedPriorityQueue()
{
    Cancel();
}

void Enqueue(T item)
{
    if (done)
    {
        return;
    }

    std::lock_guard<std::mutex> lock(mtx);
    deque.push_back(item);
    cv.notify_one();
}

但是,可以通过互斥锁将变量done(原子bool)与锁定机制分开?

要取消队列,我使用这种结构:

void Cancel()
{
    if (done)
    {
        return;
    }
    done = true;

    cv.notify_all();
}

下面这些设计的最佳解决方案是什么?

// A)
void Enqueue(T item)
{
    if (done)
    {
        return;
    }

    {
        std::lock_guard<std::mutex> lock(mtx); // lock is released before notify call
        deque.push_back(item);
    }
    cv.notify_one();
}

// B)
void Enqueue(T item)
{
    {
        std::lock_guard<std::mutex> lock(mtx); // done is atomic bool and protected by the lock along with data (deque)
        if (done) // atomic bool
        {
            return;
        }

        deque.push_back(item);
    }
    cv.notify_one();
}

// C)
void Enqueue(T item)
{
    {
        std::lock_guard<std::mutex> lock(mtx); // done is NOT atomic bool and is protected by the lock along with data (deque)
        if (done) // simple bool
        {
            return;
        }

        deque.push_back(item);
    }
    cv.notify_one();
}

等待员工:

bool Dequeue(T& item)
{
    std::unique_lock<std::mutex> lock(mtx);
    while (!done && deque.empty())
    {
        cv.wait(lock);
    }

    if (!deque.empty())
    {
        item = deque.front();
        deque.pop_front();
    }

    if (done)
    {
        return false;
    }
    return true;
}

2 个答案:

答案 0 :(得分:1)

为了确保正确性,您必须修改与condition_variable.wait(...)中获取锁定的“条件”相关的数据。

虽然不是规范性的,但我能找到的最明确的陈述是:

  

即使共享变量是原子的,也必须在   互斥为了正确地将修改发布到等待中   线程。

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

它非常明确地回答了您的问题!

Cancel()需要暂停mtx。在这一点上,它使原子停止帮助。

我不确定规范性声明的位置,但我确实知道MSVC ++社区就是这种情况。

notify_one()(或notify_all())时无需保持锁定,但在修改“共享状态”(在本例中为队列或标志)时必须保留锁定。

答案 1 :(得分:0)

正常/最常见的情况可能是队列准备就绪(未完成),而完成状态可能仅在终止期间使用。通过使用原子来优化完成的情况可能没什么意义。

您需要了解要优化的内容并使用分析器。