多线程信号

Multithreaded Signalling

本文关键字:信号 多线程      更新时间:2023-10-16

我想设计一个执行以下操作的多线程应用程序:一个线程按顺序写入循环缓冲区。然后将有n个读线程等待写入线程启动的某个信号,以唤醒并从循环缓冲区中读取。信号应该以某种方式包含一个表示要读取的循环缓冲区偏移量的整数值。在c++中可以这样做吗?如有任何帮助,我们将不胜感激。

由于我想要一种能够处理尽可能接近高速、实时流量的设计,我希望消除内存的任何分配/释放。因此,循环队列将是在启动时分配的连续内存块。我不确定你所说的排队是否符合这一点。

生产者只需要在每次有东西要写时跟踪从哪里开始写入字节的循环缓冲区数组。因此,我真正想要的是,当生产者完成了一个包含循环缓冲区中写入的最后一个字节的位置(偏移量)的写入事件时,它能传播一个"信号"。这将避免需要锁定机构。

当接收到此"传播"信号/事件时,使用者线程将唤醒。他们自己只需要跟踪他们停止的地方,然后只需读取直到信号偏移值。最后,生产者和消费者当然需要知道循环缓冲区从哪里开始以及它有多大,这样他们才能知道何时包装。

IMO,这是一种糟糕的做事方式。让生产者简单地将项目添加到循环缓冲区中。让每个读卡器等待循环缓冲区为非空。当它不为空时,读取器线程只需从缓冲区中删除下一个项目并对其进行处理。缓冲区本身应该跟踪偏移量等信息。

至于为什么这样更好:主要是因为它让系统的每个部分都做自己的事情,与系统的其他部分的交互最小。

在描述系统时,生产者需要了解队列的内部细节,以及消费者线程的基本所有细节(哪些线程在任何给定时间唤醒,哪些线程在任意给定时间空闲,哪些线程计划执行任何特定任务,等等)

我建议最大限度地减少设计,使生产商专注于生产。它对系统其余部分的了解包括一件事:一旦产生任务,如何将其放入队列。

同样,使用者线程只需要知道如何从队列中获取任务,以及如何执行该任务。

队列本身负责所有必需的线程同步。只有当任务被放入队列或从队列中删除时,才需要同步。队列本身是非常可重用的(可以用于几乎这样的生产者-消费者情况)和可替换的(例如,在基于锁的实现和无锁的实现之间进行相当琐碎的切换)。

线程调度留给操作系统——空闲的使用者线程只是在队列上等待,操作系统决定唤醒哪个线程来执行特定任务。如果它们当前都没有空闲,那么操作系统也已经"知道"了这一点,并让它们进行当前处理,直到完成并再次在队列上等待。

总结:您的建议使系统的三个部分都更加复杂。更糟糕的是,它将三者交织在一起,因此很难单独处理其中的任何一个部分。

有了这种设计,设计的每个部分都会变得简单得多,它们中的每一个都与其他部分保持着相当的隔离,因此每个部分都可以与其他部分隔离地进行处理、推理等。

根据您最近的编辑,我建议使用有界队列。

有界队列是一个具有特定长度的队列,因此队列在开始时是完全分配的,所有元素都将根据元素的默认构造函数进行初始化,或者为null,无论您希望它是什么。

从生产者端:如果队列未满,则将元素推回队列。

从消费者端:如果队列不是空的,则从队列中弹出一个元素并进行处理

你不需要用这种方式在生产者和消费者之间发送信息。事实上,让各种线程以某种方式进行通信会产生大量开销,而这些开销只会随着线程的增多而变得更加复杂。

队列本身需要是线程安全的,SO中有关于如何在C++中创建线程安全的有界队列的示例。

编辑:

你可以把你想要的任何东西放在队列中。在您的情况下,我建议使用指针队列,因为指针的大小在整个执行过程中都是恒定的。这允许您在前面分配队列,但意味着您必须在运行时为数据报进行分配。

你关于线程安全的想法或多或少是正确的。在某些情况下,多个线程访问单个变量是可以的,通常是在他们不修改变量,而只是读取变量时。即使你使用循环缓冲区,循环缓冲区也必须是线程安全的,因为任何两个生产者或消费者都会对循环缓冲区进行更改。

也就是说,每个线程对循环缓冲区或队列所需的访问时间将非常短-应该有足够的时间将信息复制到数据结构中或从数据结构中复制出来,仅此而已。您对数据的所有其他计算都可以在不锁定其他线程的数据结构的情况下完成。

如果希望多个线程同时访问数据,则可能需要考虑创建多个队列或缓冲区。也许每个生产者/消费者对有一个循环缓冲区,或者每个输入流有一个队列。。。不管发生了什么。没有一个更具体的例子很难说。

编辑2

这里有一个指向线程安全队列的链接。我不确定这是否有帮助,但看起来很有希望。