C# 中的并行任务库 (TPL)
内容提要
TPL是C#中的任务并行库,用于简化编写并行和异步代码的过程。它提供了Task类和各种功能和构造,如并行循环、async和await关键字以及协调多个任务的方法。异步编程允许单个线程处理多个任务,而并行编程将任务划分为子任务并使用多个线程同时执行以提高性能。TPL还提供了任务延续、任务工厂和附加子任务等功能。通过使用相关类型和强制转换,可以从任务中获取数据。
延伸解读
异步与并行:目标不同,别混用
文章明确区分了异步编程和并行编程。异步编程主要关注响应能力,让单个线程在等待慢操作(如文件读取、网络请求)时不被阻塞,从而释放线程去处理其他任务;并行编程则把任务拆成子任务,用多个线程同时执行,以提高CPU密集型计算的性能。理解这一区别有助于在实际开发中正确选择技术手段,避免为了并行而并行,导致不必要的线程开销和复杂度。
任务延续的“即发即弃”特性
使用ContinueWith创建延续任务时,默认不会等待上一个任务完成,而是立即开始运行,无论上一个任务是成功还是异常,这种行为被称为“即发即弃”。这意味着如果延续任务依赖于前一个任务的结果,必须通过访问previousTask.Result来获取,但这也可能引发阻塞或异常传播问题。开发者需要留意延续任务的启动时机,避免因未等待而导致的逻辑错误或资源竞争。
父子任务同步:分离与附加的区别
文章通过示例展示了分离子任务和附加子任务的行为差异。默认情况下,父任务不会等待分离的子任务完成,因此输出中“Outer task completed.”可能先于“Detached task completed.”出现。而通过指定TaskCreationOptions.AttachedToParent,父任务会等待所有附加子任务完成后再结束。这一机制允许开发者根据需求控制任务间的依赖关系,但需注意附加子任务可能影响父任务的完成时间,甚至导致异常传播。
异常处理:AggregateException与Flatten
当使用Task.Wait()等待任务时,任务中抛出的异常会被包装在AggregateException中传播。文章示例展示了如何通过捕获AggregateException并遍历InnerExceptions来处理特定异常。对于附加子任务,异常可能嵌套多层,此时可以使用Flatten()方法将嵌套的AggregateException展平,从而简化异常处理逻辑。正确使用这些方法能避免异常被忽略或错误处理,提升程序的健壮性。
Q&A
什么是C#中的任务并行库(TPL)?
TPL是C#中的任务并行库,用于简化编写并行和异步代码的过程。
如何使用Task类创建并启动任务?
可以使用Task.Run方法创建和启动任务,例如:Task task1 = Task.Run(() => { return true; });
异步编程和并行编程有什么区别?
异步编程允许单个线程处理多个任务,主要关注响应能力;而并行编程将任务划分为子任务,使用多个线程同时执行以提高性能。
如何处理任务中的异常?
可以使用Task.Wait()方法处理任务中的异常,异常类型为AggregateException。
什么是任务延续(ContinueWith)?
ContinueWith方法用于指定在上一个任务完成后应运行的延续任务,允许创建一系列任务。
如何使用Task.Factory.StartNew创建任务?
可以使用Task.Factory.StartNew方法在一个操作中创建并启动任务,例如:Task.Factory.StartNew(() => DoComputation(1.0));