【问题标题】:Optimal sleep time in multiple producer / single consumer model多生产者/单消费者模型中的最佳睡眠时间
【发布时间】:2010-11-26 21:38:13
【问题描述】:

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

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

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

不幸的是,拥有

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

【问题讨论】:

    标签: multithreading producer-consumer


    【解决方案1】:

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

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

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

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

    【讨论】:

    • 我没有做单个队列的原因是我的主要优先级是能够让生产者写入队列并尽快完成(代码用于日志库) .也许我想太多了,单个队列同样快 - 但理想情况下,记录器将支持从几个到 40-50 个线程。在多队列情况下,只有 2 个线程争用同一个资源,而不是 40-50 个线程争用一个队列锁。我确实喜欢信号量的想法,但我同意这可能是处理事情的“正确”方式。
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 我想避免强制所有生产者锁定一个队列,因为我的主要目标是最小化生产者线程发送消息所需的时间。移动到单个阻塞队列将强制执行此行为。
      【解决方案4】:

      在我看来,您似乎无意中通过让消费者线程在其他地方忙碌而引入了一些缓冲,无论是在睡觉还是在做实际的工作。 (队列充当缓冲区)也许在生产者端做一些简单的缓冲会减少你的争用。

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

      你能显示一些代码吗?

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

      【讨论】:

      • 嗯,我不完全确定你的意思。 有意缓冲的,即消费者交换队列以最小化它必须持有生产者的互斥锁的时间。这是工作相关的,所以我真的不想复制和粘贴代码,但是交换操作只是交换两个指针,就像你期望的那样 - temp = queue2,queue2 = queue1,queue1 = temp。没什么特别的,互斥锁只在被释放之前为该操作保留。
      • 是的,但是如果您的问题是锁争用,您需要缓冲(本地到您的生产者)以减少尝试获取锁的次数。
      • 另外,您应该考虑@George Phillips 的回答。也许你需要改变你的方案。
      猜你喜欢
      • 1970-01-01
      • 2017-02-24
      • 2015-04-05
      • 2021-07-18
      • 1970-01-01
      • 2011-03-12
      • 2019-05-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多