【问题标题】:trying to understand boost queue examples [closed]试图理解提升队列示例[关闭]
【发布时间】:2016-03-29 20:17:44
【问题描述】:

我正在尝试了解 boost.org 中的队列示例。具体来说最简单的,单生产者单消费者队列。考虑下面的代码。

为什么 consumer_count 是原子的而不是 producer_count?它们都在各自的线程中发生变化。

在声明队列的那一行为什么尖括号中有一个常数?我认为在构造 foo<bar> 中,bar 只是 foo 的修饰符,例如 Vector<double>。我知道它正在设置队列的大小,但为什么像普通函数调用那样使用尖括号而不是括号?

查看生产者中 for 循环中的空 while 循环。如果队列已满,则 push 函数返回 false。这会不会进入无限循环?

我不明白无锁的概念。什么是锁?

为什么消费者中有2个while pop循环?

如果生产者和消费者以相同的优先级同时运行,队列的大小应该悬停在 0 或 1 附近,对吧?还是它会填满然后一遍又一遍地倾倒?

在加入消费者线程之前,在 main 中设置了 Done。我很困惑。这段代码接近尾声的事件顺序是什么?

#include <boost/thread/thread.hpp>
#include <boost/lockfree/spsc_queue.hpp>
#include <iostream>
#include <boost/atomic.hpp>
int producer_count = 0;
boost::atomic_int consumer_count (0);
boost::lockfree::spsc_queue<int, boost::lockfree::capacity<1024> > spsc_queue;
const int iterations = 10000000;
void producer(void)
{
    for (int i = 0; i != iterations; ++i) {
        int value = ++producer_count;
        while (!spsc_queue.push(value));
    }
}
boost::atomic<bool> done (false);
void consumer(void)
{
    int value;
    while (!done) {
        while (spsc_queue.pop(value)) ++consumer_count;
    }

    while (spsc_queue.pop(value)) ++consumer_count;
}
int main(int argc, char* argv[]) {
    using namespace std;
    cout << "boost::lockfree::queue is ";
    if (!spsc_queue.is_lock_free()) cout << "not ";
    cout << "lockfree" << endl;

    boost::thread producer_thread(producer);
    boost::thread consumer_thread(consumer);

    producer_thread.join();
    done = true;
    consumer_thread.join();

    cout << "produced " << producer_count << " objects." << endl;
    cout << "consumed " << consumer_count << " objects." << endl;
}

【问题讨论】:

  • 一篇文章中有太多问题。你肯定需要一些关于 C++/Boost 的书。

标签: c++ multithreading boost queue


【解决方案1】:

单一生产者,单一消费者说明了一切:它具有原子性只要所有写入都发生在一个线程上,所有读取都发生在另一个线程上。这是合同

由于内存排序¹,合约让位于大量优化。

为什么 consumer_count 是原子的而不是 producer_count?它们都在各自的线程中发生变化。

不确定。考虑询问样本的作者。²

在声明队列的那一行为什么尖括号中有一个常数?我认为在构造 foo 中,bar 只是 foo 的修饰符,比如 Vector。我知道它正在设置队列的大小,但为什么像普通函数调用那样使用尖括号而不是括号?

环形缓冲区具有静态容量。它在尖括号中,因为它是模板参数,而不是函数参数。再次以静态方式执行操作可为编译器提供更多可在编译时进行优化的知识。

无论目标如何,决定什么是静态和动态是 API 设计选择。

查看生产者中 for 循环中的空 while 循环。如果队列已满,则 push 函数返回 false。这会不会进入无限循环?

是的。这在无锁处理中是正常的。另一种选择是......阻塞直到情况得到解决,但这会引入:锁。锁具有更高的延迟开销。因此,可以优先考虑繁忙循环的成本,以支持尽可能低的延迟。

我不明白无锁的概念。什么是锁?

请参阅我的上一段。因此,无锁在单核系统上毫无意义。在每个线程都可以在专用 CPU 内核上运行的系统上最有意义,任务通常花费很少的时间,低延迟比能源效率更重要,并且没有后台进程干扰最佳 CPU 利用率。

为什么消费者中有2个while pop循环?

第一次运行直到done 被重置。如果还有剩余元素,第二个会排空队列。

如果生产者和消费者以相同的优先级同时运行,队列的大小应该悬停在 0 或 1 附近,对吧?还是它会填满然后一遍又一遍地倾倒?

这取决于时间,因此未定义:它取决于 CPU 架构、系统负载、管道效率、缓存失效等。是的,考虑到消费者/生产者基本上都是无操作循环,您可能期望系统是能够在队列平均n 项的情况下获得一些“稳定”的加载节奏。

在加入消费者线程之前,在 main 中设置了 Done。我很困惑。这段代码接近尾声的事件顺序是什么?

生产者运行直到完成。该线程加入主线程。完成即重置。第一个消费者循环退出。如果队列中还有更多剩余元素,则第二个消费者循环会消耗它们。消费者线程结束。该线程已加入主线程。


¹ 具体来说,消费者中的 memory_order acquire 和生产者中的 release,但这些是您无需担心的实现细节,只要您满足使用要求即可

【讨论】:

  • 谢谢谢赫。回到生产者中的无限while循环。我想看看如果队列满了会发生什么。所以我对其进行了更改,以便将一个包含 2 个值的数组推送到队列中,而不仅仅是一个。我仍然只弹出 1。我认为它已经填满了,因为我推入了 20,000,000 个值,但只返回了大约 16,000,000 个(它因运行而异。)但代码仍然结束。为什么 push 返回 false 时不会卡在那个无限循环中?
  • 因为done 标志被重置。生产者旋转只要消费者需要为所有元素腾出足够的空间
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-04
相关资源
最近更新 更多