在不使用螺旋锁的情况下暂停空队列中的线程

时间:2015-11-14 07:53:56

标签: c++ multithreading performance spinlock

我有一些像这样的代码:

std::queue<int> a_queue;
bool exit = false;

void MainThreadFunc(int somedata)
{
    a_queue.push(somedata);
}

void WorkerThreadFunc()
{
    while (true)
    {
        if (exit)
            return;

        while (a_queue.empty());

        DoSomethingWithData(a_queue.front());
        a_queue.pop();
    }
}

问题是我的CPU占用率非常高,这似乎是自旋锁的结果,而工作人员什么也没做。我试图使用互斥锁,但是当队列中没有任何东西时,它需要主线程锁定它(当然,这不可能发生)。有什么替代方法可以防止这种情况发生?

3 个答案:

答案 0 :(得分:0)

以下代码是我在其他地方之前学到的。它是一个阻塞队列工具。线程可以安全地将元素放入队列,如果线程在空时尝试从队列中获取元素,它将被阻塞,直到某个其他线程将元素放入队列。希望它可以帮到你

#include <queue>
#include <cassert>
#include <mutex>
#include <condition_variable>
#include <thread>
template<typename T>
class BlockingQueue
{
private:
    std::mutex _mutex;
    std::condition_variable _condvar;
    std::queue<T> _queue;
public:
    BlockingQueue(): _mutex(),_condvar(),_queue()
    {

    }
    BlockingQueue(const BlockingQueue& rhs) = delete;
    BlockingQueue& operator = (const BlockingQueue& rhs) = delete;

    void Put(const T& task)
    {
        {
            std::lock_guard<std::mutex> lock(_mutex);
            _queue.push(task);
        }
        _condvar.notify_all();
    }

    T Take()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _condvar.wait(lock,[this]{return !_queue.empty(); });
        assert(!_queue.empty());
        T front(std::move(_queue.front()));
        _queue.pop();

        return front;
    }

};

答案 1 :(得分:0)

鉴于规定的要求,线程调度程序/上下文切换的成本太高,通常最好的办法就是像现在一样简单地刻录循环,以满足最严格的延迟需求。

原子CAS旋转/忙碌循环基本上是一种轮询方法,并且通常与轮询相关,它具有占用CPU的趋势。然而,它并没有支付你想要避免的调度程序的成本,在16ms的最后期限内完成你需要做的所有事情并提供一个框架。通常,这是满足这种截止日期的最佳选择。

对于拥有生动世界和不断动画内容的游戏,通常没有任何事情发生的冗长空闲时段。因此,一般认为游戏不断使用CPU是完全可以接受的。

根据您的要求,可能更高效的问题不是如何降低CPU利用率,以确保CPU利用率达到一个好的目的。通常,并发队列可以提供无锁,无阻塞的查询,以检查队列是否为空,您似乎已经给出了这一行:

while (a_queue.empty());

当队列为空时,你可能会偷偷摸摸地计算一些东西。通过这种方式,您不会仅仅忙于等待队列变为非空的循环。

同样,通常你的问题的理想答案将涉及一个条件变量(它取决于上下文切换和线程被唤醒)。它通常是让线程进入休眠状态(降低CPU利用率)并在所需时间唤醒的最快方法,但考虑到严格的延迟要求,最好忘记CPU利用率并更多地关注确保它达到了一个好的目的。

答案 2 :(得分:0)

std::queue不是线程安全的。正如@Hurkyl发布的那样,如果您想使用std::queue,则需要锁定。

按照编码,如果多个线程正在等待队列中的数据,他们可以所有对来自a_queue.fronf()的相同值执行操作,然后多次调用a_queue.pop()与队列中推送的值的数量无关。

请参阅Thread safety for STL queue