2

我正在编写一个具有多个生产者、单个消费者模型的应用程序(多个线程将消息发送到单个文件编写器线程)。

每个生产者线程包含两个队列,一个用于写入,一个用于读取。消费者线程的每个循环,它遍历每个生产者并锁定该生产者的互斥体,交换队列,解锁并从生产者不再使用的队列中写出。

在消费者线程的循环中,它在处理完所有生产者线程后休眠一段指定的时间。我立即注意到的一件事是,当我从 1 个生产者线程移动到 2 个生产者线程时,生产者将某些内容写入队列并返回的平均时间显着增加(增加了 5 倍)。随着添加更多线程,这个平均时间会减少,直到它触底out - 10 个制作人与 15 个制作人花费的时间没有太大区别。这大概是因为有更多的生产者要处理,生产者线程的互斥体的争用较少。

不幸的是,拥有 < 5 个生产者是应用程序相当常见的场景,我想优化睡眠时间,以便无论存在多少生产者,我都能获得合理的性能。我注意到,通过增加睡眠时间,我可以在生产者数量少的情况下获得更好的性能,但对于生产者数量较多的情况,性能会更差。

有没有其他人遇到过这种情况,如果有,您的解决方案是什么?我试过用线程数来调整睡眠时间,但它似乎有点机器特定的并且相当反复试验。

4

4 回答 4

2

您可以根据生产者的数量选择睡眠时间,甚至可以根据一些动态方案调整睡眠时间。如果消费者醒来并且没有工作,则将睡眠时间加倍,否则减半。但是将睡眠时间限制在某个最小值和最大值。

无论哪种方式,您都在解决一个更基本的问题。睡眠和轮询很容易做到正确,有时是唯一可用的方法,但它有很多缺点并且不是“正确”的方法。

您可以通过添加一个信号量来朝着正确的方向前进,每当生产者将项目添加到队列时,信号量就会增加,而当消费者处理队列中的项目时,信号量就会减少。消费者只有在有物品需要处理时才会醒来,并且会立即这样做。

但是,轮询队列可能仍然是一个问题。您可以添加一个新队列,该队列引用任何包含项目的队列。但它提出了一个问题,即为什么你没有一个消费者处理的队列而不是每个生产者的队列。在其他条件相同的情况下,这听起来是最好的方法。

于 2009-09-09T17:02:54.243 回答
1

我建议您的消费者在生产者发出信号的条件下阻止,而不是睡觉。在兼容 posix 的系统上,您可以使其与 pthread_cond 一起工作。为每个生产者创建一个数组pthread_cond_t,然后创建一个在它们之间共享的附加数组。生产者首先发信号通知他们各自的条件变量,然后是共享的。消费者等待共享条件,然后遍历数组元素,对数组的pthread_cond_timed_wait()每个元素执行 a(用于pthread_get_expiration_np()获取“现在”的绝对时间)。如果等待返回 0,则该生产者已写入数据。消费者在再次等待之前必须重新初始化条件变量。

通过使用阻塞等待,您将最大限度地减少消费者不必要地锁定生产者的时间。如前一个答案所述,您也可以使用信号量来完成这项工作。在我看来,与条件相比,信号量简化了语义,但你必须小心地为每次通过消费者循环时处理的每个生产者减少共享信号量一次。条件变量的优点是,如果您在发出信号后重新初始化它们,您基本上可以像布尔信号量一样使用它们。

于 2009-12-02T19:25:32.647 回答
0

尝试在您用于编程的语言中找到阻塞队列的实现。对于任意数量的生产者和一个消费者来说,不超过一个队列就足够了。

于 2009-09-09T16:34:53.633 回答
0

对我来说,这听起来像是您通过让消费者线程在其他地方忙于睡觉或做实际工作而意外引入了一些缓冲。(队列充当缓冲区)也许在生产者端做一些简单的缓冲会减少你的争用。

您的系统似乎对生产者和消费者之间的锁争用高度敏感,但我很困惑为什么这样一个简单的交换操作会占用足够的 cpu 时间来显示在您的运行统计信息中。

你能显示一些代码吗?

编辑:也许即使没有工作可做,您也会锁定并交换队列?

于 2009-09-09T16:55:55.920 回答