我想设计一个执行以下操作的多线程应用程序: 一个线程顺序写入循环缓冲区。然后将有n个读取器线程等待写入器线程启动的某些信号唤醒并从循环缓冲区读取。信号应该以某种方式包含一个整数值,表示要读取的循环缓冲区偏移量。这可以用c ++做吗? 任何帮助将不胜感激。
由于我希望设计能够尽可能快地处理高速实时流量,因此我希望消除内存的任何分配/释放。因此循环队列将是在启动时分配的连续内存块。我不确定你所指的排队是否符合这一点。
生产者只需跟踪每次写入内容时开始写入循环缓冲区字节数组的位置。 所以我真正要求的是生产者在完成包含写入循环缓冲区的最后一个字节的位置(偏移量)的写入事件时传播“信号”的方法。这样可以避免需要锁定机制。
当收到此“传播”信号/事件时,消费者线程将被唤醒。他们自己只需要跟踪他们离开的位置,然后只需读取信号偏移值即可。 最后,生产者和消费者当然需要知道循环缓冲区的开始位置和大小,以便知道何时进行包装。
答案 0 :(得分:2)
这是IMO,做事的一种不好的方式。让生产者只需将项添加到循环缓冲区。让每个读者在循环缓冲区上等待非空。当它非空时,读者线程只是从缓冲区中删除下一个项目并对其进行处理。缓冲区本身应该跟踪偏移等事情。
至于为何更好:主要是因为它让系统的每个部分都做自己的事情,与系统的其他部分的交互最少。
在您描述系统时,生产者需要知道队列的内部细节,以及消费者线程的基本上所有细节(在任何给定时间唤醒哪些线程,哪些线程在任何时候都是空闲的给定时间,安排执行任何特定任务,等等。)
我建议最小化的设计让制作人致力于制作。它对系统其余部分的唯一知识包含一件事:如何在生成任务后将任务放入队列。
同样,消费者线程只需要知道如何从队列中获取任务,以及如何执行该任务。
队列本身负责所有必需的线程同步。只有在从队列中放入/删除任务时才需要进行同步。队列本身是可重用的(可用于几乎这样的生产者 - 消费者情况)和可替换的(例如,在基于锁和无锁的实现之间进行相当简单的切换)。
线程调度留给操作系统 - 空闲的消费者线程只需等待队列,操作系统决定唤醒哪一个执行特定任务。如果它们当前都没有空闲,操作系统也已经“知道”了,并让它们进行当前处理,直到一个完成并再次等待队列。
总结:您的建议使得系统的三个部分中的每个部分都变得更加复杂。更糟糕的是,它将三者交织在一起,因此很难单独使用这些部分。
通过这种设计,设计的每个部分都基本上更简单,和它们中的每一个都与其他部分保持完全隔离,因此每个部分都可以与之隔离,合理等等。其他人。
答案 1 :(得分:0)
根据您最近的编辑,我建议使用有界队列。
有界队列是一个具有特定长度的队列,所以队列在开始时全部分配,所有元素都将根据元素的默认构造函数进行初始化,或者为null,无论你想要什么是。
从生产者方面:如果队列未满,则将元素推送回队列。
从消费者方面:如果队列不为空,则从队列中弹出一个元素并进行处理。
您不需要以这种方式在您的制作人和消费者之间发送消息。事实上,让你的各种线程以某种方式进行通信会产生大量的开销,只有更多的线程才会变得更复杂。
队列本身需要是线程安全的,并且在SO上有关于如何在C ++中创建线程安全的有界队列的例子。
修改强>
您可以将任何想要的内容放入队列中。我会在你的情况下建议一个指针队列,因为指针的大小在整个执行过程中都是不变的。这允许您预先分配队列,但这意味着您必须在运行时分配数据报。
您对线程安全的想法或多或少都是正确的。在某些情况下,多个线程可以访问单个变量 - 通常是在它们不修改变量时,只是读取它。即使您使用循环缓冲区,循环缓冲区也必须是线程安全的,因为任何两个生产者或使用者都将对循环缓冲区进行更改。
话虽如此,每个线程对循环缓冲区或队列所需的访问时间将非常小 - 应该有足够的时间将信息复制到数据结构中或从数据结构中复制出来,就是这样。您可以在不将数据结构与其他线程锁定的情况下完成对数据的所有其他计算。
如果您希望多个线程同时访问数据,您可能需要考虑创建多个队列或缓冲区。也许每个生产者/消费者对一个循环缓冲区,或每个输入流一个队列......无论它是什么。没有更具体的例子,很难说。
修改2
Here's a link to a thread-safe queue。我不确定它是否会有所帮助,但看起来很有希望。