【问题标题】:what's the proper way to use a ThreadPool?使用 ThreadPool 的正确方法是什么?
【发布时间】:2013-01-09 00:39:24
【问题描述】:

如果我对 ThreadPool 工作方式的理解是正确的,那么它的目的之一就是限制在给定时间可以创建的进程中的工作线程数。例如,如果将 MaxThreads 设置为 5,然后调用 QueueUserWorkItem 30 次,则会向 ThreadPool 发出 30 个请求,但其中只有 5 个请求会被新线程处理,而其他 25 个请求将被添加到队列中并在先前的请求完成并且现有线程可用时一次提供一个服务。

然而,在下面的代码中,对 Thread.Sleep(-1) 的调用保证 DoSomething() 方法永远不会返回,这意味着当前线程永远不会对后续请求可用。

但我对 ThreadPool 工作方式的理解可能不正确,因为如果正确,下面的代码将只打印数字 0-4,而不是 0-29。

有人可以解释一下 ThreadPool 是如何工作的,以及为什么下面的代码没有做我认为应该做的事情吗?

    static void DoSomething(object n)
    {
        Console.WriteLine(n);
        Thread.Sleep(-1);
    }

    static void Main(string[] args)
    {
        ThreadPool.SetMaxThreads(5, 5);
        for (int x = 0; x < 30; x++)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(DoSomething), x);
        }
        Console.Read();
    }

【问题讨论】:

  • 如果您使用的是 .NET Framework 4.0 或 4.5,则有一个轻量级库,即任务并行库。 TPL 让它变得简单轻量
  • 如果我运行你的代码,我只会看到 0-4。也许这在 .Net 4.5 中有所改变?

标签: c# multithreading queue threadpool


【解决方案1】:

ThreadPool.SetMaxThreads(5, 5)

表示活动线程数为5(如果你有超过5个cpu核心),并不意味着ThreadPool只能创建5个线程。 ThreadPool 最大线程数 = CPU Core * 250。

Thread.Sleep之后,线程处于非活动状态,不会影响其他线程的执行。

【讨论】:

  • 这有帮助。如果我将 Sleep() 替换为无限循环,则会出现不同的行为。仍然不是我的预期,但不同......
  • 这个公式是最大线程数,即 MaxThreads=(CPU*250) 是在线程或 ThreadPool 类的某处定义的吗?
【解决方案2】:

但我对 ThreadPool 工作方式的理解并不正确, 因为如果它是正确的,下面的代码将只打印数字 0-4,而不是 0-29。

是的,您的假设非常正确。

由于你在一个 ThreadPool 中排队了 30 个作业,并且这些作业将无限期休眠,它们永远不会完成,ThreadPool 类将等待一定的时间间隔来创建新线程,但不会超过最大线程数。

注意

Console.Read() 使您的后台线程保持活动状态。

文章

来自 MSDN

许多应用程序创建的线程会花费大量时间 睡眠状态,等待事件发生。其他线程可能 进入睡眠状态只是周期性地被唤醒来轮询一个 更改或更新状态信息。线程池使您能够使用 通过为您的应用程序提供一个 由系统管理的工作线程。一个线程监控 排队到线程池的几个等待操作的状态。当一个 等待操作完成,线程池中的一个工作线程 执行相应的回调函数。


当所有线程池线程都分配给任务时,线程 池不会立即开始创建新的空闲线程。避免 不必要地为线程分配堆栈空间,它会创建新的空闲 间隔线程。间隔目前是半秒, 尽管它可能会在 .NET Framework 的未来版本中发生变化。


托管线程池中的线程是后台线程。那 是,它们的 IsBackground 属性为真。这意味着一个 ThreadPool 线程毕竟不会让应用程序继续运行 前台线程已退出。

【讨论】:

    【解决方案3】:

    通常,ThreadPool 创建的线程数等于 CPU 内核数。没有必要创建更多线程,因为核心一次只能处理一个线程。但是,当排队到 ThreadPool 的任务执行时间超过 0.5 秒时,ThreadPool 会创建一个额外的线程来处理队列中的剩余任务。因此,如果您将大量繁重的任务排队到 ThreadPool,它将创建大量额外的线程来模拟多任务处理并“并行”执行所有任务。但是总执行时间与没有额外线程的情况相同,而且,因为创建线程是相当繁重的操作,所以它会更少。这就是为什么建议将 ThreadPool 用于小型任务,以避免创建实际上不会带来任何优势的额外线程。

    您可以在Albahari's 文章中阅读有关 ThreadPool 的更多信息。实际上,他在那里有很多关于线程的文章。

    【讨论】:

    • 我对这两个句子感到困惑:“线程池创建的线程数等于 CPU 核心数”和“线程池最大线程数 = CPU 核心 * 250。”(如在接受的答案中所写上面)哪一个是对的?
    • 两者。线程数等于 CPU 内核数,通常只要遵循指导原则就足够了,即使用 ThreadPool 进行快速计算操作。但是,如果您设法加载其所有线程,然后将另一个任务排队,它将等待大约 0.5 秒以释放任何现有线程,如果没有一个线程被释放,它将创建一个额外的线程来处理您的任务。然后,当所有任务都完成并释放线程时,它会杀死多余的线程。
    【解决方案4】:

    可能是 Thread.Sleep(-1) 没有达到您的预期。

    参数 Int32: 线程被阻塞的毫秒数。指定零 (0) 表示应该挂起该线程以允许其他等待线程执行。指定 Infinite 无限期阻塞线程。

    http://msdn.microsoft.com/en-us/library/d00bd51t.aspx

    你应该看看 Tasks,http://msdn.microsoft.com/en-us/library/dd235608.aspx 把它想象成 Threadpool 2.0

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-11-24
      • 2021-09-23
      • 2017-04-07
      • 2013-03-19
      • 2019-05-11
      • 2018-02-17
      • 2022-01-07
      • 2012-05-17
      相关资源
      最近更新 更多