《C#本质论》读书笔记(18)多线程处理
Posted 【唐】三三
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《C#本质论》读书笔记(18)多线程处理相关的知识,希望对你有一定的参考价值。
.NET Framework 4.0 看(本质论第3版)
.NET Framework 4.5 看(本质论第4版)
.NET 4.0为多线程引入了两组新API:TPL(Task Parallel Library,任务并行库)和PLINQ(Parallel LINQ,并行LINQ)。
18.1 独立线程的运行和控制
通过 System.Threading.Tasks.Task 类在托管代码中公开各种API,该类代表的是一个异步操作。然而,一个 Task 并不直接映射到一个非托管线程。相反, Task 为底层的非托管线程构造提供了一定程度的抽象。
不是每次创建安一个 Task 时会创建一个线程。相反, Task 会从线程池请求一个线程。线程池针对 Task 请求,会判断是否需要创建一个全新线程,还是分配一个现有的线程。
通过将线程概念抽象为 Task ,开发人员不必操心何时创建一个新的操作系统线程,何时重用一个现有线程。换言之,降低了高效管理线程所涉及的复杂性。
编写 Task 时,需要分配希望 Task 执行的一组指令,然后启动 Task 。指令的分配基本上都靠委托。 |
- class Program
- {
- static void Main(string[] args)
- {
- const int Repettitions = 10000;
- Task task = new Task(() =>
- {
- for (int count = 0; count < Repettitions; count++)
- {
- Console.Write(\'-\');
- }
- });
- task.Start();
- for (int count = 0; count < Repettitions; count++)
- {
- Console.Write(\'+\');
- }
- task.Wait();
- }
- }
程序在声明了 Task 之后,执行了一个 Start() 调用。除非执行这个调用,否则为 Task 指定的 Action 是不会开始执行的。 task.Wait() 调用强迫主线程(正在执行的第2个 for 循环线程)停止,并“等待”分配给 task 的所有工作执行完。
问题:我什么时候能使用Task的Start()方法?
- 问题:我什么时候能使用Task的Start()方法?只有 Task 处于 TaskStatus.Created 状态时才能使用实例方法 Start() 。
并且,只有在使用 Task 的公共构造函数构造的 Task 实例才能处于 TaskStatus.Created 状态。 - 问题:使用Task.Run()/Task.ContinueWith()/Task.Factory.StartNew()/TaskCompletionSource/异步方法(即使用async与await关键字的方法)……应该调用Start()方法吗?
不应该。不仅不应该,而且也不能,因为此时调用Start()会报异常。从问题1可知:Start()实例方法只适用于TaskStatus.Created状态的Task。由上面提到的方式创建的Task其状态不是TaskStatus.Created,而是如TaskStatus.WaitingForActivation、TaskStatus.Running或TaskStatus.RanToCompletion。
如果在任务执行的操作要返回一个结果,那么对结果的任何请求都会被自动阻塞(block),直至任务完成。
- static void Main(string[] args)
- {
- Task<string> task = Task.Factory.StartNew<string>(
- > PiCalculator.Calculate(100));
- foreach (char busySymbol in Utility.BusySymbols())
- {
- if (task.IsCompleted)
- {
- Console.Write(\'\\b\');
- break;
- }
- Console.Write(busySymbol);
- }
- Console.WriteLine();
- // Blocks until task completes.
- Console.WriteLine(task.Result);
- System.Diagnostics.Trace.Assert(task.IsCompleted);
- }
这个代码显示的任务数据类型是 Task<TResult> (本例具体是一个string)。任务的泛型版本包含一个 Result 属性,可通过它获取 Task<TResult> 所执行的 Func<TResult> 的返回值。
代码中没有 task.Start() 调用,使用了静态 Factory 属性的 StartNew() 方法。结果和实例化 Task 差不多,只是从 Task.Factory.StartNew<TResult>(Func<TResult> function) 返回后,线程已经启动了。是同 StartNew() 能满足你的几乎一切要求,除非要将 Task 的实例化和调度分开。
Task 的属性:
属性 | 解释 | 说明 |
Status | 指明任务的状态。
Created = 0,
WaitingForActivation = 1,
WaitingToRun = 2,
Running = 3,
WaitingForChildrenToComplete = 4,
RanToCompletion = 5,
Canceled = 6,
Faulted = 7, |
获取此任务的 TaskStatus 枚举 |
IsComplete | 不管任务是否出错,IsComplete在任务完成后都被设为true,任何时候只要Status为 5,6,7 就为true。 | 获取此 Task 是否已完成 |
Id | 解决多线程问题(竞争和死锁),Id尤为有用 | 获取此 Task 实例的 ID |
AsyncState | 能跟踪额外的数据。 | 获取在创建 Task 时提供的状态对象,如果未提供,则为 null |
Task.CurrentId | 任何地方都可调用,特别适合调试和诊断性类型的活动 | 返回当前正在执行 Task 的 ID |
AyncState 举例:假定多个任务要计算一个 List<T> 的值。为了将结果放在列表中正确位置。将准备包含结果的那个列表索引村吃到 AsyncState 属性中。这样在任务结束后,代码可以使用 AsyncState (先转换成 int)访问列表中特定索引位置。(注意,调用 List.Add() 不是一个跨多线程的安全操作,可能造成竞态条件,进而造成数据丢失。)
Task声明周期
- public enum TaskStatus
- {
- // 该任务已初始化,但尚未被计划。
- Created = 0,
- // 该任务正在等待 .NET Framework 基础结构在内部将其激活并进行计划。
- WaitingForActivation = 1,
- // 该任务已被计划执行,但尚未开始执行。
- WaitingToRun = 2,
- // 该任务正在运行,但尚未完成。
- Running = 3,
- // 该任务已完成执行,正在隐式等待附加的子任务完成。
- WaitingForChildrenToComplete = 4,
- // 已成功完成执行的任务。
- RanToCompletion = 5,
- // 该任务已通过对其自身的 CancellationToken 引发 OperationCanceledException 异常
- Canceled = 6,
- // 由于未处理异常的原因而完成的任务。
- Faulted = 7,
- }
- class Program
- {
- static void Main(string[] args)
- {
- /* 创建一个任务 不调用 不执行 状态为Created */
- Task tk = new Task(() =>
- {
- });
- Console.WriteLine(tk.Status.ToString());
- /* 创建一个任务 执行 状态为 WaitingToRun */
- Task tk1 = new Task(() =>
- {
- });
- tk1.Start(); /*对于安排好的任务,就算调用Start方法也不会立马启动 此时任务的状态为WaitingToRun*/
- Console.WriteLine(tk1.Status.ToString());
- /* 创建一个主任务 */
- Task mainTask = new Task(() =>
- {
- SpinWait.SpinUntil(() =>
- {
- return false;
- }, 30000);
- });
- /* 将子任务加入到主任务完成之后执行 */
- Task subTask = mainTask.ContinueWith((t1) =>
- {
- });
- /* 启动主任务 */
- mainTask.Start();
- /* 此时子任务状态为 WaitingForActivation */
- Console.WriteLine(subTask.Status.ToString());
- /* 创建一个任务 执行 后 等待一段时间 并行未结束的情况下 状态为 Running */
- Task tk2 = new Task(() =>
- {
- SpinWait.SpinUntil(() => false, 30000);
- });
- tk2.Start(); /*对于安排好的任务,就算调用Start方法也不会立马启动*/
- SpinWait.SpinUntil(() => false, 300);
- Console.WriteLine(tk2.Status.ToString());
- JAVA编程思想读书笔记--多线程