Answers:
如果您有很多需要不断处理的逻辑任务,并且希望并行执行,请使用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#线程
至少阅读“入门”部分。该电子书提供了很好的介绍,还包括大量高级线程信息。
知道是否使用线程池只是一个开始。接下来,您将需要确定哪种线程池输入方法最适合您的需求:
这本电子书解释了所有这些内容,并建议何时使用它们以及创建自己的线程。
线程池旨在减少线程之间的上下文切换。考虑一个运行着多个组件的过程。这些组件中的每一个都可以创建工作线程。进程中的线程越多,上下文切换浪费的时间就越多。
现在,如果每个组件都将项目排队到线程池中,则上下文切换的开销将大大减少。
线程池旨在最大程度地跨CPU(或CPU内核)完成工作。这就是默认情况下线程池为每个处理器增加多个线程的原因。
在某些情况下,您不想使用线程池。如果您正在等待I / O或正在等待事件等,那么您将占用该线程池线程,其他任何人都无法使用它。同样的想法也适用于长时间运行的任务,尽管构成长时间运行任务的是主观的。
Pax Diablo也很不错。加速线程不是免费的。这需要时间,并且它们会占用额外的内存以用于其堆栈空间。线程池将重新使用线程以分摊此费用。
注意:您询问有关使用线程池线程下载数据或执行磁盘I / O的问题。为此,您不应该使用线程池线程(由于上述原因)。而是使用异步I / O(也称为BeginXX和EndXX方法)。因为FileStream
那是BeginRead
and EndRead
。因为HttpWebRequest
那是BeginGetResponse
and EndGetResponse
。它们使用起来更加复杂,但是它们是执行多线程I / O的正确方法。
为了在并发执行单元时获得最高性能,请编写自己的线程池,在启动时创建线程对象池,然后进入阻塞状态(以前挂起),等待上下文运行(具有标准接口的对象,由您的代码)。
关于任务与线程与.NET ThreadPool的许多文章未能真正为您提供决策性能所需的条件。但是当您比较它们时,线程胜出,尤其是线程池。它们在CPU之间分布最好,并且启动速度更快。
应该讨论的是Windows(包括Windows 10)的主要执行单元是线程,并且OS上下文切换开销通常可以忽略不计的事实。简而言之,我无法找到许多此类文章的令人信服的证据,无论该文章声称通过节省上下文切换或提高CPU使用率来实现更高的性能。
现在考虑一下现实:
我们大多数人不需要我们的应用程序具有确定性,并且我们大多数人没有线程的强硬背景,例如,线程通常是在开发操作系统时附带的。我上面写的内容不适合初学者。
因此,最重要的是讨论易于编程的东西。
如果创建自己的线程池,则需要做一些编写工作,因为您需要关注跟踪执行状态,如何模拟挂起和恢复以及如何取消执行-包括在整个应用程序范围内关掉。您可能还需要考虑是否要动态扩展池,以及池将具有什么容量限制。我可以在一个小时内编写一个这样的框架,但这是因为我已经做了很多次了。
编写执行单元的最简单方法也许就是使用Task。Task的优点在于您可以创建一个并在代码中直接将其启动(尽管可能需要谨慎)。您可以传递取消令牌来处理要取消的任务。同样,它使用promise方法链接事件,您可以让它返回特定类型的值。而且,有了async和await,存在更多选项,您的代码将更易于移植。
从本质上讲,了解Tasks与Threads与.NET ThreadPool的优缺点非常重要。如果需要高性能,我将使用线程,而我更喜欢使用自己的池。
比较的一种简单方法是启动512线程,512任务和512 ThreadPool线程。您会发现从Threads开始会有延迟(因此,为什么要编写线程池),但是所有512个Threads将在几秒钟内运行,而Tasks和.NET ThreadPool线程要花几分钟才能全部启动。
以下是这种测试的结果(具有16 GB RAM的i5四核),每30秒运行一次。执行的代码在SSD驱动器上执行简单的文件I / O。
我通常在需要在另一个线程上执行某些操作时就使用Threadpool,而实际上并不关心它何时运行或结束。诸如记录日志或什至在后台下载文件之类的东西(尽管有更好的方法来执行异步样式)。我需要更多控制权时使用自己的线程。当我有多个需要在大于1个线程中处理的命令时,我发现使用Threadsafe队列(自行攻击)来存储“命令对象”也很不错。因此,您可能会拆分Xml文件并将每个元素放入队列中,然后让多个线程对这些元素进行一些处理。我在uni(VB.net!)中写了这样的队列方式,已经转换为C#。我出于特殊原因将其包括在下面(此代码可能包含一些错误)。
using System.Collections.Generic;
using System.Threading;
namespace ThreadSafeQueue {
public class ThreadSafeQueue<T> {
private Queue<T> _queue;
public ThreadSafeQueue() {
_queue = new Queue<T>();
}
public void EnqueueSafe(T item) {
lock ( this ) {
_queue.Enqueue(item);
if ( _queue.Count >= 1 )
Monitor.Pulse(this);
}
}
public T DequeueSafe() {
lock ( this ) {
while ( _queue.Count <= 0 )
Monitor.Wait(this);
return this.DeEnqueueUnblock();
}
}
private T DeEnqueueUnblock() {
return _queue.Dequeue();
}
}
}