【问题标题】:How to setup and manage persistent multiple threads?如何设置和管理持久多线程?
【发布时间】:2010-12-10 06:36:07
【问题描述】:

我考虑使用 POSIX 来实现,尽管这个问题更多的是关于架构。

我从一个有几个主要工作要做的更新循环开始。我可以将这些作业分为四个或五个具有共同内存访问要求的主要任务。我的想法是将这些作业分解为它们自己的线程,并让它们完成一个“更新”周期并休眠到下一帧。

但是如何同步呢?如果我在每个循环开始时分离 4 或 5 个线程,让它们运行一次,死掉,然后在每次通过时分离另外 4-5 个线程?听起来很贵。

创建这些线程一次听起来更合理,然后让它们进入睡眠状态,直到同步调用将其唤醒。

这是明智的做法吗?我愿意接受从想法到任何形式的实施的回应。

编辑:根据到目前为止的答案,我想补充一下:

  • 需要并发
  • 这些工作线程旨在在非常短的时间内运行
  • 每个线程所做的工作总是相同的
  • 我正在考虑 4-5 个线程,20 个是硬限制。

【问题讨论】:

    标签: c multithreading


    【解决方案1】:

    这取决于线程正在执行的任务的粒度。如果他们正在执行长时间的任务(例如一秒或更长时间),那么与线程正在执行的工作相比,创建和销毁线程的成本可以忽略不计,因此我建议保持简单并按需创建线程。

    相反,如果您的任务非常短(例如少于 10-100 毫秒左右),您肯定会开始注意到创建和销毁大量线程的成本。在这种情况下,是的,您应该只创建一次线程并让它们休眠,直到工作到达。为此,您需要使用某种condition variable(例如pthread_cond_t):线程等待条件变量,当工作到达时,您向条件变量发出信号。

    【讨论】:

    • 为此 +1。我会接受你的回答,但 john 提出了一个更接近我需要的实现。
    【解决方案2】:

    如果您总是在每个周期都要做相同的工作,并且您需要等待所有工作完成后再开始下一个周期,那么您正在考虑正确的解决方案。

    您需要一些同步对象:“帧开始信号量”、“帧结束信号量”和“帧结束事件”。如果每帧有 n 个独立任务,则启动 n 个线程,循环如下所示(伪代码):

    while true:
      wait on "start of frame semaphore"
      <do work>
      enter lock
      decrement "worker count"
      if "worker count" = 0 then set "end of frame event"
      release lock
      wait on "end of frame semaphore"
    

    然后您可以运行控制器线程:

    while true:
      set "worker count" to n
      increment "start of frame semaphore" by n
      wait on "end of frame event"   
      increment "end of frame semaphore" by n
    

    这适用于小 n。如果每个周期需要完成的任务数量变大,那么您可能会希望使用线程池和任务队列,这样您就不会用线程压倒系统。但是该解决方案的复杂性更高,而线程复杂性是敌人。

    【讨论】:

    • 嗨,约翰。我认为这听起来像是满足我需求的合理方法。我认为自己不会超过 12 个线程。特别是因为如果 N 变得太大,我可以选择合并线程。为了清楚起见,在这个实现中,工作线程总是处于活动状态,但只有一个信号量会解开代码?或者posix库会暂停(睡眠)线程,然后重新激活信号量上的线程?控制器循环只是设置信号量的状态,对吗?
    • 我不确定您所说的“活跃”是什么意思。等待信号量会导致线程进入睡眠状态,直到信号量增加。在释放信号量之前,线程不应消耗任何 CPU 或调度程序资源。控制器线程只是用来设置信号量的状态。通过一些额外的复杂性,可以消除该线程,但复杂性是敌人,通常无论如何您都有其他事情要做该线程。 :)
    【解决方案3】:

    最好的可能是使用任务队列。

    任务队列可以看作是等待作业提交给它们的线程。如果一次发送多个,则按先进先出顺序执行。

    这样,您维护 4-5 个线程,每个线程执行您提供给它们的作业,而无需为每个作业分离一个新线程。

    唯一的问题是我不知道 C 中任务队列的许多实现。Apple 的 Grand Central Dispatch 就是这样做的; FreeBSD 也有它的实现。除了这些,我不知道其他的。 (不过,我看起来并不难。)

    【讨论】:

      【解决方案4】:

      您的想法被称为线程池。它们可以在 WinAPI、Intel TBB 和 Visual Studio ConcRT 中找到,我对 POSIX 了解不多,因此无法为您提供帮助,但它们是具有许多理想属性的出色结构,例如出色的缩放,如果发布的工作可以分开。

      但是,我不会轻视这项工作所花费的时间。如果您有五个任务,并且您遇到的性能问题非常严重,以至于多线程是关键,那么创建线程几乎可以肯定是一个可以忽略不计的问题。

      【讨论】:

      • 谢谢,感谢您的警告。尽管在这种情况下,我并没有分解一项任务,因为它花费的时间太长。我正在尝试分割作为并发主要候选者的不同区域。
      • @bitcruncher:并发只有一个目标:性能。如果不需要性能,就不需要并发。
      • 是的,当然。我想我想说的是,在我实现的这一点上,我并没有试图重构现有的单一任务。我正在为共享相似内存空间并且需要尽快执行的未来任务奠定基础。
      猜你喜欢
      • 1970-01-01
      • 2021-05-29
      • 2015-01-13
      • 1970-01-01
      • 1970-01-01
      • 2021-11-30
      • 2015-04-04
      • 2012-04-18
      • 2013-09-24
      相关资源
      最近更新 更多