【问题标题】:Thread vs ThreadPool线程与线程池
【发布时间】:2010-09-18 19:17:03
【问题描述】:

使用新线程和使用线程池中的线程有什么区别?有哪些性能优势,为什么我应该考虑使用池中的线程而不是我明确创建的线程?我在这里专门考虑 .NET,但一般示例都可以。

【问题讨论】:

    标签: .net multithreading architecture threadpool


    【解决方案1】:

    线程本地存储对于线程池来说不是一个好主意。它赋予线程一个“身份”;不再是所有线程都是平等的。现在,如果您只需要一堆相同的线程,准备好在没有创建开销的情况下完成您的工作,线程池就特别有用。

    【讨论】:

      【解决方案2】:

      线程池将为频繁且相对较短的操作提供好处

      • 重用已经创建的线程而不是创建新线程(一个昂贵的过程)
      • 当对新工作项的请求激增时限制线程创建速率(我相信这仅在 .NET 3.5 中)
        • 如果您将 100 个线程池任务排队,它只会使用已创建的线程数来服务这些请求(例如 10 个)。线程池会进行频繁的检查(我相信在 3.5 SP1 中每 500 毫秒),如果有排队的任务,它会创建一个新线程。如果您的任务很快,那么新线程的数量会很少,并且为短任务重用 10 个左右的线程将比预先创建 100 个线程更快。

        • 如果您的工作负载始终有大量线程池请求进入,那么线程池将通过上述进程在池中创建更多线程来调整自己以适应您的工作负载,以便有更多线程可供处理请求

        • 查看Here 以获取有关线程池如何在后台运行的更多详细信息

      如果作业运行时间相对较长(可能大约一两秒,但取决于具体情况),自己创建一个新线程会更合适

      @Krzysztof - 线程池线程是后台线程,将在主线程结束时停止。手动创建的线程默认为前台(在主线程结束后会继续运行),但可以在对其调用 Start 之前设置为后台。

      【讨论】:

      • 我唯一想知道的是来自 MSDN (msdn.microsoft.com/en-us/library/1c9txz50.aspx) 的以下语句“仅当执行的前台线程数小于处理器数时才会执行后台线程。”。那么这是否意味着在内核之间划分工作时,前台线程具有优先权?
      • ➤ 您不能中止或中断线程池中的线程。 ➤ 您不能加入线程池中的线程。为此,您必须使用其他一些机制
      • 基本疑问:谁将为线程池创建初始线程?是否有任何默认设置?
      【解决方案3】:

      在这里查看之前的帖子:

      When should I not use the ThreadPool in .Net?

      总结是,如果您需要生成许多短期线程,Threadpool 是不错的选择,而使用 Threads 可以让您获得更多控制权。

      【讨论】:

        【解决方案4】:

        如果您需要很多线程,您可能想要使用 ThreadPool。他们重复使用线程,从而节省了创建线程的开销。

        如果你只需要一个线程来完成某件事,线程可能是最简单的。

        【讨论】:

          【解决方案5】:

          还有

          new Thread().Start()

          如果你关闭你的程序,生成的前台线程不会死掉。 ThreadPool 线程是在您关闭应用程序时终止的后台线程。

          【讨论】:

          • 您始终可以将线程设置为后台。默认情况下它们只是前景。
          • nemo: var t = new Thread( ... ); t.BackgroundThread = true; t.Start();
          • 关于“程序”一词的说明。桌面应用程序运行在一个进程中,并且至少有一个前台线程来管理 UI。只要该进程有前台线程,它就会继续运行。当您关闭桌面应用程序时,前台 UI 线程会停止,但如果它有其他前台线程,您不一定停止进程
          【解决方案6】:

          一般来说(我从未使用过 .NET),线程池将用于资源管理目的。它允许将约束配置到您的软件中。也可能出于性能原因这样做,因为创建新线程的成本可能很高。

          也可能有系统特定的原因。在 Java 中(同样我不知道这是否适用于 .NET),线程的管理器可能会在每个线程从池中拉出时应用线程特定的变量,并在返回时取消设置它们(传递类似身份)。

          示例约束: 我只有 10 个 db 连接,所以我只允许 10 个工作线程访问数据库。

          这并不意味着您不应该创建自己的线程,但在某些情况下使用池是有意义的。

          【讨论】:

            【解决方案7】:

            .NET 托管线程池:-

            • 根据当前工作负载和可用硬件调整大小
            • 包含工作线程完成端口线程(专门用于服务 IO)
            • 针对大量相对短暂的操作进行了优化

            存在其他可能更适合长时间运行的操作的线程池实现。

            具体来说,使用线程池来防止您的应用创建太多线程。线程池最重要的特性是工作队列。也就是说,一旦你的机器足够繁忙,线程池会将请求排队,而不是立即产生更多线程。

            因此,如果您要创建少量有限数量的线程,请自行创建它们。如果您无法预先确定可能会创建多少线程(例如,它们是为响应传入的 IO 而创建的),并且它们的工作将是短暂的,请使用线程池。如果您不知道有多少,但他们的工作将长期运行,那么平台中没有什么可以帮助您 - 但您可能能够找到适合的替代线程池实现。

            【讨论】:

            • 在 .NET 中,可以在没有线程池的情况下使用完成端口吗?我假设异步 I/O 方法是唯一的方法(在 .NET 中)并且它们使用线程池
            【解决方案8】:

            如果您不知道或无法控制将创建多少线程,则使用池是个好主意。

            只是在列表控件的positionchanged 事件上使用线程更新数据库中的某些字段的表单存在问题(避免冻结)。我的用户花了 5 分钟从数据库中收到错误(与 Access 的连接太多),因为他更改列表位置的速度太快了...

            我知道还有其他方法可以解决基本问题(包括不使用访问权限),但池是一个好的开始。

            【讨论】:

              【解决方案9】:

              adpool 线程的主要需求是处理预期几乎立即完成的短小任务。硬件中断处理程序通常在不适合非内核代码的堆栈上下文中运行,但硬件中断处理程序可能会发现应该尽快运行用户模式 ​​I/O 完成回调。为了运行这样的事情而创建一个新线程将是巨大的矫枉过正。有一些预先创建的线程可以被分派来运行 I/O 完成回调或其他类似的事情会更有效。

              此类线程的一个关键方面是,如果 I/O 完成方法始终基本上立即完成并且从不阻塞,并且当前运行此类方法的此类线程的数量至少等于处理器的数量,那么唯一的方法如果其他方法之一阻塞或其执行时间超过正常的线程时间片,则任何其他线程都可以在上述方法之一完成之前运行;如果按预期使用线程池,那么这些都不会经常发生。

              如果一个方法不能在它开始执行后 100ms 左右退出,则该方法应该通过主线程池以外的其他方式执行。如果有很多任务要执行,这些任务是 CPU 密集型但不会阻塞,使用与“主”线程池分开的应用程序线程池(每个 CPU 内核一个)调度它们可能会有所帮助,因为使用运行非阻塞 CPU 密集型任务时,线程多于内核将适得其反。但是,如果一个方法需要一秒钟或更长时间才能执行,并且大部分时间都被阻塞,那么该方法应该很可能在专用线程中运行,并且几乎可以肯定不应该在主线程池线程中运行。如果需要由 I/O 回调之类的东西触发长时间运行的操作,则应该在回调之前为长时间运行的操作启动一个线程,并让它在回调脉冲的监视器上等待,或者让回调在回调退出时启动一个新线程来执行操作,从而有效地将自己的线程返回到线程池。

              【讨论】:

                【解决方案10】:

                我很好奇这些资源的相对使用情况,并在我的 2012 双核英特尔 i5 笔记本电脑上使用 Windows 8 上的 .net 4.0 版本运行了一个基准测试。线程池平均需要 0.035 毫秒才能启动线程占用的时间。平均为 5.06 毫秒。换句话说,对于大量短期线程,池中的线程启动速度大约快 300 倍。至少在测试范围(100-2000)线程中,每个线程的总时间似乎相当稳定。

                这是进行基准测试的代码:

                    for (int i = 0; i < ThreadCount; i++) {
                        Task.Run(() => { });
                    }
                
                    for (int i = 0; i < ThreadCount; i++) {
                        var t = new Thread(() => { });
                        t.Start();
                    }
                

                【讨论】:

                • 我认为这是因为 ThreadPool 重用了创建的线程而不是创建新的线程(这很昂贵)
                【解决方案11】:

                线程

                1. 创建线程远比使用线程池慢。
                2. 您可以更改线程的优先级。
                3. 与资源相关的进程中的最大线程数。
                4. 线程处于操作系统级别,由操作系统控制。
                5. 当任务相对较少时,使用 Thread 是更好的选择 长期运行

                线程池

                1. 在线程池上运行线程比直接运行要快得多 创建线程。
                2. 您不能更改线程运行的优先级基于 线程池。
                3. 每个进程只有一个线程池。
                4. 线程池由 CLR 管理。
                5. 线程池对于短期操作很有用。
                6. 线程池中的线程数与应用有关 加载。
                7. TPL 任务基于线程池运行

                【讨论】:

                • 我想这些是 .NET 特定的,我可以在我的 C++ 程序中创建任意数量的线程池,并且始终可以更改优先级(对于线程的线程部分可能不是一个好主意游泳池,不过)。
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2010-12-22
                • 2012-09-03
                • 2023-03-28
                • 2012-07-26
                • 1970-01-01
                • 1970-01-01
                • 2018-10-02
                相关资源
                最近更新 更多