Multithreading 为什么使用队列作为生产者和消费者之间的通信缓冲区?

Multithreading 为什么使用队列作为生产者和消费者之间的通信缓冲区?,multithreading,producer-consumer,Multithreading,Producer Consumer,我看到了许多使用队列作为共享数据缓冲区的生产者-消费者模式的描述和示例。使用队列而不是单元素数据缓冲区的原因是什么 在每个生产者-消费者实现中,要么生产者生成数据的速度快于消费者处理数据的速度,要么生产者生成数据的速度慢于消费者处理数据的速度。这意味着队列最终将填充到其最大容量,或者在消费者消费该数据元素之前,它将只包含一个数据元素。一旦队列已满,生产者将需要等待消费者,然后才能添加另一个数据元素。当队列仅包含一个元素时,使用者需要等待生产者,然后才能使用另一个元素。换句话说,队列的行为类似于单

我看到了许多使用队列作为共享数据缓冲区的生产者-消费者模式的描述和示例。使用队列而不是单元素数据缓冲区的原因是什么

在每个生产者-消费者实现中,要么生产者生成数据的速度快于消费者处理数据的速度,要么生产者生成数据的速度慢于消费者处理数据的速度。这意味着队列最终将填充到其最大容量,或者在消费者消费该数据元素之前,它将只包含一个数据元素。一旦队列已满,生产者将需要等待消费者,然后才能添加另一个数据元素。当队列仅包含一个元素时,使用者需要等待生产者,然后才能使用另一个元素。换句话说,队列的行为类似于单个元素缓冲区

与处理单个数据元素相比,队列操作需要更多的处理开销和程序复杂性。应该有某种优势来证明增加的复杂性和开销是合理的

生产者-消费者模式有两大子模式。第一种是同步生产者-消费者模式。此模式确保使用者使用使用者生成的每个数据元素,并且只使用一次。上面讨论了同步模式。第二个是抽样消费者

采样消费者仅使用生产者生成的数据元素样本。如果使用者消耗数据元素的速度快于生产者生产数据元素的速度,则称使用者对缓冲区进行过采样。如果消费者消费数据元素的速度慢于生产者生产数据元素的速度,则称消费者对数据元素采样不足。欠采样设计的一个例子是气象站,它以100Hz(每秒100次)的速率生成温度读数,同时以1Hz(每秒1次)的速率生成报告。消费者不需要每秒读取100个数据点,而是产生这些读数的平均值。相反,它可以每秒读取1次并报告该读数。如果使用者仅对1%的读数进行采样,则无需在缓冲区中提供数据元素队列

有界队列通常用于控制内存消耗。无界队列总是有可能耗尽堆内存,从而导致应用程序失败

当有许多生产者时,有界多元素队列似乎是一个好主意。如果需要以完全相同的方式处理由多个生产者产生的所有数据,那么将所有数据写入公共缓冲区以便一个或多个消费者可以处理数据可能是合理的。多元素队列有一些相关的成本。多元素队列使用内存的速率大约等于每个数据元素的大小乘以有界队列中元素的数量。多元素队列需要比单个元素队列更复杂的逻辑。复杂性总是正确性的敌人

当使用多元素队列时,使用者总是读取队列中存在时间最长的数据元素。队列毕竟是一种FIFO数据结构。生产者将新元素写入队列,直到队列填满。此时,生产者要么等待队列上的空间,要么覆盖队列上最新的元素。消费者仍然读取队列中最早的元素,无论生产者在做什么

当队列填充时,队列的行为与单个元素队列的行为完全相同。在阻塞生产者/消费者模式中,满队列迫使生产者等待消费者从队列中读取。单个元素队列始终为满或空。一个完整的多元素队列只包含许多等待被消费的数据元素,以及一个被消费的元素

单元素队列只是消除了队列中等待符合条件的数据的时间

在模式的使用者端,使用者只能读取队列中最早的元素,而不管队列中有多少元素。某个元素对使用者可用或不可用。消费者看不到队列的大小。如果队列为空,则使用者必须暂停等待可用数据,或者必须主动对队列进行采样以获取可用数据。如上所述,对于采样队列,使用者可以简单地处理队列上最早的值,并且队列从不将自身标记为空

使用多元素队列实现采样消费者是非常困难的。单元素队列就可以了。生产者只是覆盖队列中的任何内容,消费者读取队列中的任何内容

下面是用Ada编写的采样生产者/消费者模式的示例

------------------------------------------------------------------
--抽样消费者--
------------------------------------------------------------------
使用Ada.Text_IO;使用Ada.Text\u IO;
程序采样_PC是
受保护的Buf是
程序写入(项目:整数);
函数读取返回整数;
程序设置完成;
函数Get_Done返回布尔值;
私有的
值:整数:=Integer'First;
是否完成:布尔:=False;
结束Buf;
保护体Buf是
过程写入(项:整数)为
开始
值:=项目;
结束写入;
函数读取返回整数为
开始
返回值;
结束读取;
程序集_已完成
开始
是否完成:=真;
结束设置_完成;
函数Get_Done返回布尔值为
开始
返回已完成;
完成任务;
结束Buf;