使用spsc_queue来避免单生产者单消费者程序中的繁忙等待

Avoiding busy waiting in a Single Producer Single Consumer Program viz using spsc_queue

本文关键字:程序 消费者 忙等待 生产者 使用 queue spsc      更新时间:2023-10-16

我试图实现单个生产者(主线程)和单个消费者(从主线程派生的子线程)问题,因此根据我的搜索,我得到了spsc_queue作为boost库提供的最佳无锁数据结构。现在从他们的示例代码中,消费者函数看起来像这样:

void consumer(void)
{
    int value;
    while (!done) {
        while (queue.pop(value))
            ++consumer_count;
    }
    while (queue.pop(value))
        ++consumer_count;
}

现在,可能会发生spsc_queue may remain empty for sometime,所以为了避免繁忙的等待,我在代码中引入了睡眠:

void consumer(void)
{
    int value;
    while (!done) {
        if (spsc_queue.empty())
        {
            cout << "Waiting for data....n";
            this_thread::sleep_for (chrono::milliseconds(100));
        }
        else
        {
            while (spsc_queue.pop(value))
            {
                ++consumer_count;
            }
        }
    }
    while (spsc_queue.pop(value))
        ++consumer_count;
}

这样做是正确的吗?或者,有没有更好的方法?我遇到了一些图书馆,如libeventlibevboost::asio::io_service -有人能帮我找出最好的方法来避免忙碌的等待吗?

我关心的是性能和代码必须是无锁和无等待(如果可能的话)。

你的目标和你的需求不一致。

wait-free:等待直到一个元素已经可用,将排除此属性。

lock-free:你的目标是在元素可用之前不做任何工作,即你想要阻塞。这再次与无等待和无锁相矛盾。

你真正想要的是像

这样的东西
if (spsc_queue.empty()) {
     doSomethingElse();
}

或者简单地继续忙循环。

也许最接近你想要的是:

if (spsc_queue.empty()) {
    std::this_thread::yield();
}

重新调度线程,让其他线程完成它们的工作。但是,您将放弃您的时间片,并且可能无法在25-100ms之前重新安排。

为什么代码必须是无锁的?既然队列为空的概率似乎相当高,为什么需要无锁代码呢?如果那样的话,你没有什么好处。

另一方面,如果空队列的概率很低,那么繁忙循环会随着时间的推移而摊销。无论如何,您不会在繁忙循环中花费太多时间,但要尽可能快地取出元素(以偶尔的繁忙等待为代价)。