我一直在尝试用C#学习多线程编程,我很困惑什么时候最好使用线程池而不是创建我自己的线程.一本书建议仅为小任务使用线程池(无论这意味着什么),但我似乎无法找到任何真正的指导方针.在做出此编程决策时,您使用了哪些注意事项?
我建议您在C#中使用线程池,其原因与任何其他语言相同.
如果要限制运行的线程数或不希望创建和销毁它们的开销,请使用线程池.
通过小任务,您阅读的书意味着任务的寿命很短.如果创建一个只运行一秒的线程需要十秒钟,那么你应该使用池的一个地方(忽略我的实际数字,这是计算的比率).
否则,您将花费大量时间来创建和销毁线程,而不是简单地完成他们打算做的工作.
如果您有许多需要持续处理的逻辑任务,并且您希望并行完成,请使用pool + scheduler.
如果您需要同时进行与IO相关的任务,例如从远程服务器或磁盘访问下载内容,但需要每隔几分钟执行一次,那么请创建自己的线程并在完成后终止它们.
编辑:关于一些注意事项,我使用线程池进行数据库访问,物理/模拟,AI(游戏),以及在处理大量用户定义任务的虚拟机上运行的脚本任务.
通常,一个池由每个处理器2个线程组成(现在很可能是4个),但是如果你知道需要多少线程,你可以设置你想要的线程数量.
编辑:创建自己的线程的原因是由于上下文更改(当线程需要交换进出进程时,以及它们的内存).无用的上下文更改,比如当你没有使用你的线程时,只要让他们坐在那里就像人们所说的那样,可以很容易地使你的程序性能降低一半(比如你有3个睡眠线程和2个活动线程).因此,如果那些下载线程只是在等待,那么他们就会占用大量CPU并为您的实际应用程序冷却缓存
以下是.Net中线程池的一个很好的总结:http://blogs.msdn.com/pedram/archive/2007/08/05/dedicated-thread-or-a-threadpool-thread.aspx
当你不应该使用线程池并开始自己的线程时,帖子也有一些要点.
我强烈推荐阅读这本免费的电子书: Joseph Albahari在C#中的线程化
至少阅读"入门"部分.电子书提供了很好的介绍,并包含了大量高级线程信息.
知道是否使用线程池只是一个开始.接下来,您需要确定哪种方法最适合您的线程池:
任务并行库(.NET Framework 4.0)
ThreadPool.QueueUserWorkItem
异步代理
的BackgroundWorker
这本电子书解释了这些,并建议何时使用它们而不是创建自己的线程.
线程池旨在减少线程之间的上下文切换.考虑一个运行多个组件的进程.这些组件中的每一个都可以创建工作线程.进程中的线程越多,上下文切换浪费的时间就越多.
现在,如果每个组件都将项目排队到线程池,那么上下文切换开销就会少得多.
线程池旨在最大限度地提高CPU(或CPU内核)的工作量.这就是为什么默认情况下,线程池会为每个处理器旋转多个线程.
在某些情况下,您不希望使用线程池.如果您正在等待I/O,或等待事件等,那么您将占用该线程池线程,并且其他任何人都无法使用它.同样的想法适用于长时间运行的任务,尽管构成长期运行的任务是主观的.
Pax Diablo也是一个很好的观点.旋转线程不是免费的.它需要时间,并且它们为堆栈空间消耗额外的内存.线程池将重新使用线程来分摊此成本.
注意:您询问是否使用线程池线程来下载数据或执行磁盘I/O. 您不应该使用线程池线程(出于上面概述的原因).而是使用异步I/O(又名BeginXX和EndXX方法).对于FileStream
那将是BeginRead
和EndRead
.对于HttpWebRequest
那将是BeginGetResponse
和EndGetResponse
.它们使用起来更复杂,但它们是执行多线程I/O的正确方法.
请注意.NET线程池可能阻止其处理的任何重要,变量或未知部分的操作,因为它易于线程饥饿.考虑使用.NET并行扩展,它在线程操作上提供了大量的逻辑抽象.它们还包括一个新的调度程序,它应该是对ThreadPool的改进.看到这里