0

我写了一个共享优先级队列类。
为了发出停止提供数据的信号,我使用方法Cancel(),它将标志设置done为 false,并且不允许应用程序从队列中写入/读取任何数据。我不确定与andstd::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通过互斥锁将变量(原子布尔)与锁定机制分开吗?

要取消队列,我使用此构造:

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;
}
4

2 回答 2

2

为确保正确性,您必须修改与“条件”有关的数据,该“条件”持有condition_variable..wait(...)

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

即使共享变量是原子的,也必须在互斥锁下进行修改,才能正确地将修改发布到等待线程。

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

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

Cancel()需要持有mtx。在这一点上,使其成为原子不再有帮助。

我不确定规范性声明在哪里,但我知道 MSVC++ 社区就是这种情况。

notify_one()您(或)时不需要持有锁,notify_all()但在修改“共享状态”(在本例中为队列或标志)时必须持有它。

于 2017-06-07T13:46:41.187 回答
1

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

您需要了解您正在优化什么并使用分析器。

于 2017-06-07T09:39:53.030 回答