【问题标题】:threading higher priority for a not known in advance thread为未知的提前线程线程化更高的优先级
【发布时间】:2023-03-22 05:18:01
【问题描述】:

我创建了大约 5000 个后台工作人员,他们在控制台应用程序中进行密集工作。我还使用了一个实例化对象的外部库,比如 ObjectX。在某个时刻,比如 t0,ObjectX 尝试从 os 线程池中获取一个线程并启动它,但我无法控制它如何获取该线程。对于 100 名后台工作人员来说一切正常。对于 1000 个后台工作人员,ObjectX 在 t0 之后大约需要 10 分钟才能获取并启动一个线程。

  1. 有没有办法提前为对象将来启动的任何线程设置高优先级?

  2. 我认为 1 的答案是“否”,有没有办法限制后台工作人员的优先级,以便以某种方式支持其他一切?即使我只想“支持”ObjectX。

目标是始终有可用资源来运行由 ObjectX 启动的线程,无论机器多么过载。

我在 Windows 64 位机器上使用 C# 和 .Net fr 3.5。

【问题讨论】:

  • 我们需要知道您使用的是什么语言或平台。
  • 5000 名后台工作人员...疯狂...
  • @user2161465 更多工人!= 完成更多工作。坚持合理数量的工人,比如说.. 10? (N 个内核 * 2 的上限是一个很好的经验法则)
  • @user2161465 您需要创建它们,但不需要实际运行它们。然而,这不会有成效。只需创建几个 BGW 并有一个工作队列来完成每个工作人员在空闲时从其中获取项目的工作队列,您会做得更好。这是生产者/消费者模型,在多线程编程中很常见。当你有那么多工作要做时,不要为每个工作单元创建一个 BGW。您的 CPU 将花费所有时间在线程之间切换,而不是真正进行生产性工作。
  • @user2161465 每个 Worker 线程都会消耗运行时创建、启动和切换到的开销。当你有 5000 个工作人员时,你的处理器必须处理大量的开销,并且除了管理所有工作人员之外几乎没有做任何工作。在生产者消费者中,您可以启动与拥有逻辑 CPU(物理/超线程)大致相同数量的工作人员,然后将工作项发布给他们。在这种情况下,当您需要 ObjectX 线程优先处理时,它应该在最多 100 毫秒内做出响应(可能会更短,具体取决于您的硬件)。

标签: c# .net multithreading


【解决方案1】:

线程的工作方式是操作系统为它们分配处理器时间。当这种情况发生时,这称为上下文切换。上下文切换大约需要 2000-8000 个周期(即取决于处理器 2000-8000 条指令)。如果操作系统有许多 CPU 或内核,它可能不需要将 CPU 从一个线程中取出并交给另一个线程——避免上下文切换。每个 CPU 一次只能运行一个线程,当您需要 CPU 的线程多于 CPU 时,您将强制进行上下文切换。上下文切换的执行速度不超过系统时间片(客户端每 20 毫秒,服务器每 120 毫秒)。

如果您有 5000 个后台工作人员,那么您实际上有 5000 个线程。这些线程中的每一个都可能在争夺 CPU 时间。在客户端版本的 Windows 上,这意味着每秒 250,000 次上下文切换。即每秒 500,000,000 到 2,000,000,000 个周期专门用于仅用于在线程之间切换。 (即超出您的线程正在执行的工作)如果它甚至可以每秒处理那么多上下文切换

推荐的做法是每个处理器只有一个 CPU 绑定线程。 CPU-bound 线程是一个花费很少时间“等待”的线程。 UI 线程不是 CPU 绑定线程。如果您的后台工作线程花费大量时间等待锁定,那么它们可能也不受 CPU 限制——但一般来说,后台工作线程受 CPU 限制。 (否则,使用后台工作人员有什么意义?)。

此外,操作系统会花费大量时间来确定接下来需要哪个线程来获取 CPU。当您开始更改线程优先级时,您会对此进行干预,并且大多数情况下最终会导致整个系统(不仅仅是您的应用程序)变慢而不是更快。

更新:

相对而言,创建一个新线程大约需要 200,000 个周期,而销毁一个线程大约需要 100,000 个周期。

更新 2:

如果问题的推动力不仅仅是“如果可以完成”,而是能够扩展工作负载,那么正如 @JoshW/@Servy 所提到的,使用生产者/消费者模式之类的东西可以实现可扩展性可以通过队列或服务总线促进水平扩展到多台计算机/节点。简单地启动大量线程是不可扩展的,超出了 CPU 的数量。如果您真正想要的是一种可以横向扩展的架构,因为“可用资源......机器的过载程度”根本不可能。

【讨论】:

  • +1 很好地解释了为什么使用这么多线程如此昂贵(而且不仅仅是无用)。我只想补充一点,即使在非 CPU 绑定的程序(如 I/O 绑定的程序)的情况下,每个 CPU 超过 1 个线程弊大于利(在这种情况下,线程池和相关技术(I/O O 完成端口或异步编程)将提供救援,将线程计数降至合理值
  • @demo80 by "wait" 我的意思是线程通过Wait 方法(Monitor.Enter、WaitHandle.WaitX 等)明确地将时间让给操作系统。我不会认为像 Stream.Read 这样的东西是“等待”(即“等待”IO 完成),即使它并没有真正占用 CPU……但是,是的,应该尽可能使用异步 IO。添加了有关线程创建/销毁成本的详细信息。
  • 优秀的答案,来自我的 +1。 OP 绝对应该考虑转变为生产者-消费者模式,而不是仅仅召集几千名工人。
  • @Peter 是的,当然它们在某种程度上是不同的(即使在内核中线程的命运或多或少相同,线程将从就绪队列中移出);不过,您的观点(在较小程度上)也适用于这种等待:线程是昂贵的资源,拥有太多简直是糟糕的!
  • @JoshW 是的,生产者/消费者模式将是更好的选择。它将通过队列或消息总线促进水平扩展到多台计算机。
【解决方案2】:

我个人认为这是一个坏主意,但是......鉴于您在其他答案上所做的 cmets 以及您的要求“无论创建了多少后台工作人员,ObjectX 都会尽快运行”......您可以想象强迫您的后台工作人员使用 ManualResetEvent 进行阻止。

例如,在工作代码的顶部,您可以使用 WaitOne 方法阻止手动重置事件。此手动重置可以是静态的,也可以作为输入参数传递,并且无论您的 ObjectX 被实例化/调用还是什么,您都可以在 ManualResetEvent 上调用 .Reset 方法。这将阻止 WaitOne 线上的所有工作人员。接下来在运行 ObjectX 的代码底部,调用 ManualResetEvent.Set() 方法,这将解除对工作人员的阻塞。

请注意,这不是管理线程的有效方法,但如果您“只需要让它工作”并且稍后有时间改进它......我想这是一种可能的解决方案。

【讨论】:

    【解决方案3】:

    目标是始终有可用资源来运行由 ObjectX 启动的线程,无论机器有多过载。

    那么线程优先级可能不是正确的工具..Remember, thread priorities are evil

    一般来说,windows 不是实时操作系统;特别是,win32 甚至没有尝试软实时(IIRC,NT 内核在某些时候尝试过至少支持软实时子系统,但我可能错了)。因此无法保证可用资源或时间。

    另外,您是否担心系统中的其他线程?这些线程不在您的控制范围内(如果其他线程已经处于系统最大优先级怎么办?)。 如果您担心应用程序中的线程......您可以控制和限制它们,使用更少的线程/工作人员来完成更多工作(例如,以更大的单位批量工作,并将其提交给工作人员,或者使用 TPL 或其他将为您处理和限制线程使用的工具)

    也就是说,您可以在创建线程时进行拦截(例如查看这个问题 https://stackoverflow.com/a/3802316/863564)查看它是否是为 ObjectX 创建的(例如,检查其名称)并使用 SetThreadPriority 来提升它。

    【讨论】:

    • 这不是问题的答案,也不能帮助 OP 解决他的问题。如果他不应该使用线程优先级,那么他应该应该做什么来解决他的问题?
    • @Servy,我在答案中添加了一些细节。他解决不了,windows不是实时系统。不保证资源的可用性
    • 你可以很容易地解决这个问题,只需不创建 5000 个线程,而是最多只有十几个线程。我们对他的所作所为知之甚少,无法证明如何做到这一点,但这几乎可以肯定是他需要走的路。
    • 我只担心进程中的线程。但即使我愿意,我也无法为尚不存在的线程设置优先级。这就是为什么我认为我可以为我创建的 BG 分配有限的资源。
    • You can solve the problem easily enough true,但他在编辑中澄清(在我指出 5k 线程是......疯狂:),这也是我对这个问题的第一个解决方案)5000 个线程只是过载系统的一个例子(见我的报价)。 no matter how overloaded the machine is包含失控的因素,所以没有通用的解决办法
    猜你喜欢
    • 2014-11-26
    • 2012-05-15
    • 1970-01-01
    • 1970-01-01
    • 2010-09-22
    • 2011-06-25
    • 2015-02-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多