【问题标题】:.NET Thread Pool - Unresponsive WinForms UI.NET 线程池 - 无响应的 WinForms UI
【发布时间】:2012-11-08 17:34:46
【问题描述】:

场景

我有一个 Windows 窗体应用程序。在主窗体内部有一个循环大约 3000 次,在新线程上创建一个类的新实例以执行一些计算。请记住,此设置使用线程池,当此循环只有大约 100 次迭代(要处理 100 个资产)时,UI 会保持响应。但是一旦这个数字开始大幅增加,UI 就会锁定到 eggtimer 模式,因此写入表单列表框的日志变得不可读。

问题

我认为解决此问题的最佳方法是使用后台工作人员是否正确? UI 是否被锁定是因为即使我使用了许多不同的线程(为了提高速度),但 UI 本身并不在其自己的单独线程上?

非常感谢建议的实现。

编辑!!

因此,可以说,我决定分批处理 100 个资产,而不是仅仅启动并排队处理 3000 个资产。我将如何有效地执行此操作?我之前尝试添加“Thread.Sleep(5000);”在每批 100 人被解雇之后,但整个事情似乎都糟透了....

【问题讨论】:

    标签: c# winforms backgroundworker threadpool


    【解决方案1】:

    如果您正在创建 3000 个单独的线程,则您正在推送 ThreadPool 类的 documented limitation

    如果应用程序受到突发事件的影响 的活动,其中大量的 线程池任务排队,使用 SetMinThreads 方法增加 最小空闲线程数。 否则,内置延迟 创建新的空闲线程可能会导致 瓶颈。

    请参阅该 MSDN 主题以获取针对您的情况配置线程池的建议。

    如果您的工作是 CPU 密集型工作,那么拥有这么多单独的线程将导致超出其价值的开销。但是,如果 IO 非常密集,那么拥有大量线程可能会有所帮助。

    .NET 4 引入了对并行编程的出色支持。如果这是你的选择,我建议你have a look at that

    【讨论】:

    • 我可以证明。我构建了一个应用程序,可以一次将数十个 SSH 隧道连接到各种机器。我机器上的默认线程池大小约为 12 个线程。所以当它启动时,比如50个隧道连接任务,它启动几个然后爬行,每秒只启动一两个,导致启动这50个任务需要很长时间,更不用说完成它们了。如果我使用 SetMinThreads 确保池中至少有 50 个或(2x 50)个线程,那么所有 50 个 SSH 隧道都会立即设置,不会有任何延迟。对于线程阻塞(非异步)操作尤其如此。
    【解决方案2】:

    更多线程不等于最高速度。事实上,太多的线程等于更低的速度。如果您的任务只是与 CPU 相关,那么您应该只使用与内核一样多的线程,否则您会浪费资源。

    有 3,000 次迭代,并且您的表单线程每次都尝试创建一个线程,可能发生的情况是您正在最大化线程池并且表单挂起,因为它需要等待前一个线程完成才能完成分配一个新的。

    显然ThreadPool 不能这样工作。我以前从未用线程检查过它,所以我不确定。另一种可能性是任务开始用调用淹没 UI 线程,此时它将放弃 GUI。

    【讨论】:

    • 线程池在等待新线程时不会阻塞。它将所有线程请求排队。此外,仅当工作是 CPU 密集型工作时,您关于线程数等于内核数的论点才是正确的。如果它是 IO 密集型的,那么线程可能比内核多得多是有意义的。
    • @Eric - 因此限定词“如果你的任务只是 CPU 相关”
    • 嗯。可能应该先喝完我的第一杯咖啡 :-) 抱歉读过了那个限定词。
    【解决方案3】:

    不看代码很难判断 - 但是,根据您的描述,有一个嫌疑人。

    您提到您现在在 ThreadPool 上运行它。切换到 BackgroundWorker 不会显着改变任何东西,因为它也使用 ThreadPool 来执行。 (BackgroundWorker 只是简化了调用调用...)

    话虽如此,我怀疑问题是您的通知返回到您的 ListBox 的 UI 线程。如果您的调用过于频繁,您的 UI 在尝试“赶上”时可能会变得无响应。如果您通过 Control.Invoke 将过多的状态信息反馈给 UI 线程,就会发生这种情况。

    否则,请确保您的所有工作都在 ThreadPool 上完成,并且您没有阻塞 UI 线程,它应该可以工作。

    【讨论】:

    • 我遇到了与此处描述的完全相同的问题,我有太多信息从多个线程推送到 ListView 并且应用程序很难跟进。要么实现某种队列以将状态传递给 UI,要么只是尽量不将所有内容推送到那里。
    【解决方案4】:

    如果每个线程都将某些内容记录到您的 ui 中,则每个写入的日志行都必须调用主线程。最好缓存日志输出并仅每 100 次迭代或类似的东西更新 gui。

    【讨论】:

      【解决方案5】:

      由于我没有看到你的代码,所以这只是很多猜测,带有一些很有希望的受过教育的猜测。

      线程池所做的只是将您的请求排队,然后在其他线程完成工作时触发新线程。现在 3000 个线程听起来并不多,但如果有大量的处理正在进行,你可能会破坏你的 CPU。

      我不相信后台工作人员会提供帮助,因为您最终会重新创建一个管理器来处理线程池为您提供的所有池。我认为你的更多问题是你有太多的数据分块正在进行。我认为一个好的起点是限制您启动和维护的线程数量。线程池管理器很容易让你做到这一点。找到一个平衡点,让您在处理数据的同时仍保持 UI 响应。

      【讨论】:

        猜你喜欢
        • 2013-08-04
        • 2015-05-25
        • 2018-11-11
        • 1970-01-01
        • 2011-11-25
        • 1970-01-01
        • 2021-02-20
        • 1970-01-01
        相关资源
        最近更新 更多