在生产应用程序中的所有线程进入方法上都需要一个异常处理程序,就像在主线程上一样(通常在执行堆栈中处于更高级别)。未处理的异常会导致整个应用程序关闭。与一个丑陋的对话!
在编写此类异常处理块时,很少会忽略该错误:通常,您会记录异常的详细信息,然后显示一个对话框,允许用户自动将这些详细信息提交到您的Web服务器。然后,您可能会关闭该应用程序-因为该错误有可能破坏了程序的状态。但是,这样做的代价是用户将丢失其最近的工作-例如打开的文档。
WPF和Windows Forms应用程序的“全局”异常处理事件(Application.DispatcherUnhandledException和Application.ThreadException)仅针对在主UI线程上引发的异常触发。您仍然必须手动处理工作线程上的异常。
AppDomain.CurrentDomain.UnhandledException在任何未处理的异常上触发,但没有提供防止应用程序随后关闭的方法。但是,在某些情况下,您不需要处理工作线程上的异常,因为.NET Framework会为您处理异常。这些将在接下来的部分中介绍,分别是:
每当启动线程时,都会花费数百微秒来组织诸如新鲜的私有局部变量堆栈之类的事情。每个线程(默认情况下)也消耗大约1 MB的内存。线程池通过共享和回收线程来减少这些开销,从而允许在非常细粒度的级别上应用多线程,而不会影响性能。当利用多核处理器以“分而治之”的方式并行执行计算密集型代码时,这很有用。
线程池还限制了将同时运行的工作线程总数。过多的活动线程限制了操作系统的管理负担,并使CPU缓存无效。一旦达到限制,作业将排队并仅在另一个作业完成时才开始。这使任意并发的应用程序(例如Web服务器)成为可能。 (异步方法模式是一种高级技术,通过高效利用池化线程来进一步实现这一点;我们在C#4.0的第23章“ Nutshell”中对此进行了描述)。
有多种进入线程池的方法:
以下构造间接使用线程池:
任务并行库(TPL)和PLINQ具有足够的功能和高级功能,即使在线程池不重要的情况下,您也希望使用它们来协助多线程。我们将在第5部分中详细讨论这些内容。现在,我们将简要介绍如何使用Task类作为在池线程上运行委托的简单方法。
使用池线程时需要注意以下几点:
您可以通过Thread.CurrentThread.IsThreadPoolThread属性查询当前是否在池化线程上执行。
您可以使用“任务并行库”中的“任务”类轻松地输入线程池。 Task类是在Framework 4.0中引入的:如果您熟悉较早的构造,请考虑将非通用Task类替换为ThreadPool.QueueUserWorkItem,而将通用Task <TResult>替换为异步委托。与旧版本相比,新版本的结构更快,更方便且更灵活。
要使用非泛型Task类,请调用Task.Factory.StartNew,并传入目标方法的委托:
1
2
3
4
5
6
7
8
9
|
static void Main() // The Task class is in System.Threading.Tasks { Task.Factory.StartNew (Go); } static void Go() { Console.WriteLine ( "Hello from the thread pool!" ); } |
Task.Factory.StartNew返回一个Task对象,您可以使用该对象来监视任务-例如,您可以通过调用其Wait方法来等待它完成。
调用任务的Wait方法时,所有未处理的异常都可以方便地重新抛出到主机线程中。 (如果您不调用Wait而是放弃任务,则未处理的异常将像普通线程一样关闭进程。)
通用Task <TResult>类是非通用Task的子类。它使您可以在完成执行后从任务中获取返回值。在下面的示例中,我们使用Task <TResult>下载网页:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
static void Main() { // Start the task executing: Task< string > task = Task.Factory.StartNew< string > ( () => DownloadString ( "http://www.linqpad.net" ) ); // We can do other work here and it will execute in parallel: RunSomeOtherMethod(); // When we need the task's return value, we query its Result property: // If it's still executing, the current thread will now block (wait) // until the task finishes: string result = task.Result; } static string DownloadString ( string uri) { using ( var wc = new System.Net.WebClient()) return wc.DownloadString (uri); } |