我试图了解来自boost.org的队列示例。特别是最简单的单生产者单个消费者队列。请考虑以下代码。
为什么consumer_count是原子而不是producer_count?他们都在自己的线程中改变。
在声明队列的行上为什么尖括号中有一个常量?我在构造foo<bar>
中认为,bar只是foo的修饰符,如Vector<double>
。我知道它设置了队列的大小,但是为什么尖括号而不是像正常函数调用的括号一样?
查看producer中for循环中的空while循环。如果队列填满,则push函数返回false。那么这不会进入无限循环吗?
我不理解无锁概念。什么是锁?
为什么消费者中有2个pop循环?
如果生产者&amp;消费者同时以相同的优先级运行,队列的大小应该在0或1附近徘徊?或者它是否填充然后转储和&amp;结束了吗?
在连接使用者线程之前,在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;
}
答案 0 :(得分:2)
单一生产者,单一消费者说这一切:它具有原子性长,因为所有写入恰好发生在一个线程上,并且所有写入恰好在另一个线程上。这是合同。
由于内存排序¹,合同让位于大量优化。
为什么consumer_count是原子而不是producer_count?他们都在自己的线索内改变。
不确定。考虑询问样本的作者.²
在声明队列的行上为什么尖括号中有一个常量?我认为在构造foo中,bar只是foo的修饰符,就像Vector一样。我知道它正在设置队列的大小,但为什么尖括号而不是像正常函数调用的括号?
环形缓冲区具有静态容量。它位于尖括号中,因为它是模板参数,而不是函数参数。静态地做事情为编译器提供了更多的知识,可以在编译时进行优化。
无论目标如何,决定制作静态和动态的是API设计选择。
查看producer中for循环中的空while循环。如果队列填满,则push函数返回false。那么这不会进入无限循环吗?
是。这在无锁处理中是正常的。替代方案是......阻止直到情况得到解决,但这会引入:锁定。锁具有更高的延迟开销。因此,可以优先考虑繁忙循环的成本,以支持尽可能低的延迟。
我不懂锁定概念。什么是锁?
见我上一段。因此,在单核系统上无锁是没有意义的。在每个线程可以在专用CPU核心上运行的系统中最有意义,任务通常只需要很少的时间,低延迟比能源效率更重要,并且没有后台进程干扰最佳CPU利用率。
为什么消费者中有2个pop循环?
第一次运行直到重置done
。如果有剩余的元素,第二个会排空队列。
如果生产者&amp;消费者同时以相同的优先级运行,队列的大小应该在0或1附近徘徊?或者它是否填充然后转储和&amp;结束了吗?
这取决于时间,因此是未定义的:它取决于CPU架构,系统负载,流水线效率,缓存失效等。是的,考虑到消费者/生产者基本上都是无操作循环,您可能期望系统是能够获得一些“稳定”的加载节奏,其中队列平均n
个项目。
在连接使用者线程之前,在main中设置了Done。我糊涂了。这段代码结尾附近的事件顺序是什么?
生产者一直运行到完成。该主题是主要的。完成重置。第一个消费者循环退出。如果有更多剩余元素排队,则第二个消费者循环会消耗它们。消费者线程结束。该主题在main中加入。
¹具体来说,消费者中的memory_order acquire
和生产者中的release
,但这些是您不必担心的实施细节,只要您满足使用要求