C++ 使用std::atomic的正确性<;布尔>;与std::mutex结合使用

C++ 使用std::atomic的正确性<;布尔>;与std::mutex结合使用,c++,multithreading,c++11,stdatomic,stdmutex,C++,Multithreading,C++11,Stdatomic,Stdmutex,我已经编写了一个共享优先级队列类。 为了发出停止提供数据的信号,我使用方法Cancel(),该方法将符号done设置为false,并且不允许应用程序从队列中写入/读取任何数据。我不确定是否将std::atomic与std::mutex和std::condition\u变量组合使用。我不确定,如果我的解决方案是线程安全的,或者可能发生争用情况: Enqueue方法的原始版本为: std::deque<T> deque; std::mutex mtx; std::condition_va

我已经编写了一个共享优先级队列类。
为了发出停止提供数据的信号,我使用方法
Cancel()
,该方法将符号
done
设置为false,并且不允许应用程序从队列中写入/读取任何数据。我不确定是否将
std::atomic
std::mutex
std::condition\u变量组合使用。我不确定,如果我的解决方案是线程安全的,或者可能发生争用情况:

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();
}
以下设计的最佳解决方案是什么

// 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();
}
/A)
无效排队(T项)
{
如果(完成)
{
返回;
}
{
std::lock_guard lock(mtx);//在notify调用之前释放锁
德克。推回(项目);
}
cv.通知_one();
}
//(B)
无效排队(T项)
{
{
std::lock_guard lock(mtx);//done是原子bool,由锁和数据保护(deque)
if(done)//原子布尔
{
返回;
}
德克。推回(项目);
}
cv.通知_one();
}
//(C)
无效排队(T项)
{
{
std::lock_guard lock(mtx);//done不是原子bool,它与数据一起受锁保护(deque)
if(done)//简单bool
{
返回;
}
德克。推回(项目);
}
cv.通知_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;
}
bool出列(T&item)
{
std::唯一锁(mtx);
而(!done&&deque.empty())
{
cv.等待(锁定);
}
如果(!deque.empty())
{
item=deque.front();
deque.pop_front();
}
如果(完成)
{
返回false;
}
返回true;
}

正常/最常见的情况可能是队列已就绪(未完成),而完成状态可能仅在终止期间使用。使用原子函数为已完成的情况进行优化可能没有什么意义


您需要了解您正在优化什么,并使用探查器。

正常/最常见的情况可能是队列已就绪(未完成),而完成状态可能仅在终止期间使用。使用原子函数为已完成的情况进行优化可能没有什么意义


您需要了解您正在优化的内容并使用探查器。

为了确保正确性,您必须修改与持有锁的“条件”相关的数据,该锁是
条件变量在
中获取的。等待(…)

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

即使共享变量是原子变量,也必须在 互斥,以便将修改正确发布到等待 线

在这里:

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

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

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


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

为了确保正确性,您必须修改与“条件”相关的数据持有
条件变量
.wait(…)
中获取的锁

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

即使共享变量是原子变量,也必须在 互斥,以便将修改正确发布到等待 线

在这里:

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

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

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


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

请缩小问题范围。似乎有些缺乏焦点你是什么意思?问题中有什么不清楚?我想具体说明一个问题……你在标题中问的是,在线程库中使用原子的“正确性”,这已经很模糊了。然后你在正文中问了两个问题:在条件变量之前是否需要锁定;在许多实现中,哪一个是“更好的”,哪一个也是模糊的。选择一个,并且尽可能客观地具体说明你想要的答案希望现在更好;-)我已经排除了“在条件变量之前是否需要锁定”这个问题。这段代码什么都不做,因为条件变量上没有等待任何东西。您的代码需要是最小的和完整的。这并不容易,但如果不能做到最小和完整,就意味着在理解你的问题之前,我必须编写你缺少的代码。请缩小问题的范围。似乎有些缺乏焦点你是什么意思?问题中有什么不清楚?我想具体说明一个问题……你在标题中问的是,在线程库中使用原子的“正确性”,这已经很模糊了。然后你在正文中问了两个问题:在条件变量之前是否需要锁定;在许多实现中,哪一个是“更好的”,哪一个也是模糊的。选择一个,并且尽可能客观地具体说明你想要的答案希望现在更好;-)我已经排除了“在条件变量之前是否需要锁定”这个问题。这段代码什么都不做,因为条件变量上没有等待任何东西。您的代码需要是最小的和完整的。这并不容易,但如果不能做到最小和完整,就意味着在理解您的问题之前,我必须编写您缺少的代码
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;
}