zoukankan      html  css  js  c++  java
  • 《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 。指令的分配基本上都靠委托。
          
    1. class Program  
    2. {  
    3.     static void Main(string[] args)  
    4.     {  
    5.         const int Repettitions = 10000;  
    6.         Task task = new Task(() =>  
    7.         {  
    8.             for (int count = 0; count < Repettitions; count++)  
    9.             {  
    10.                 Console.Write('-');  
    11.             }  
    12.         });  
    13.         task.Start();  
    14.         for (int count = 0; count < Repettitions; count++)  
    15.         {  
    16.             Console.Write('+');  
    17.         }  
    18.   
    19.         task.Wait();  
    20.     }  
    21. }  
     
     
    程序在声明了 Task 之后,执行了一个 Start() 调用。除非执行这个调用,否则为 Task 指定的 Action 是不会开始执行的。 task.Wait() 调用强迫主线程(正在执行的第2个 for 循环线程)停止,并“等待”分配给 task 的所有工作执行完。
     

    问题:我什么时候能使用Task的Start()方法?

     

    1. 问题:我什么时候能使用Task的Start()方法?只有 Task 处于 TaskStatus.Created 状态时才能使用实例方法 Start() 。
      并且,只有在使用 Task 的
      公共构造函数构造的 Task 实例才能处于 TaskStatus.Created 状态。

    2. 问题:使用Task.Run()/Task.ContinueWith()/Task.Factory.StartNew()/TaskCompletionSource/异步方法(即使用asyncawait关键字的方法)……应该调用Start()方法吗?
      不应该。不仅不应该,而且也不能,因为此时调用Start()会报异常。从问题1可知:Start()实例方法只适用于TaskStatus.Created状态的Task。由上面提到的方式创建的Task其状态不是TaskStatus.Created,而是如TaskStatus.WaitingForActivation、TaskStatus.Running或TaskStatus.RanToCompletion。
     
    如果在任务执行的操作要返回一个结果,那么对结果的任何请求都会被自动阻塞(block),直至任务完成。
     
    1. static void Main(string[] args)  
    2. {  
    3.     Task<string> task = Task.Factory.StartNew<string>(  
    4. > PiCalculator.Calculate(100));  
    5.   
    6.     foreach (char busySymbol in Utility.BusySymbols())  
    7.     {  
    8.         if (task.IsCompleted)  
    9.         {  
    10.             Console.Write('');  
    11.             break;  
    12.         }  
    13.         Console.Write(busySymbol);  
    14.     }  
    15.   
    16.     Console.WriteLine();  
    17.     // Blocks until task completes.  
    18.     Console.WriteLine(task.Result);  
    19.     System.Diagnostics.Trace.Assert(task.IsCompleted);  
    20. }  
     
    这个代码显示的任务数据类型是 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声明周期

    1. public enum TaskStatus  
    2. {   
    3.     // 该任务已初始化,但尚未被计划。  
    4.     Created = 0,  
    5.     // 该任务正在等待 .NET Framework 基础结构在内部将其激活并进行计划。  
    6.     WaitingForActivation = 1,  
    7.     // 该任务已被计划执行,但尚未开始执行。  
    8.     WaitingToRun = 2,  
    9.    
    10.     // 该任务正在运行,但尚未完成。  
    11.     Running = 3,  
    12.     // 该任务已完成执行,正在隐式等待附加的子任务完成。  
    13.     WaitingForChildrenToComplete = 4,  
    14.    
    15.     // 已成功完成执行的任务。  
    16.     RanToCompletion = 5,  
    17.     // 该任务已通过对其自身的 CancellationToken 引发 OperationCanceledException 异常  
    18.     Canceled = 6,  
    19.     // 由于未处理异常的原因而完成的任务。  
    20.     Faulted = 7,  
    21. }  
     
     
     
    1. class Program  
    2. {  
    3.     static void Main(string[] args)  
    4.     {  
    5.         /*  创建一个任务 不调用 不执行  状态为Created */  
    6.         Task tk = new Task(() =>  
    7.         {  
    8.         });  
    9.         Console.WriteLine(tk.Status.ToString());  
    10.   
    11.         /*  创建一个任务 执行  状态为 WaitingToRun */  
    12.         Task tk1 = new Task(() =>  
    13.         {  
    14.         });  
    15.         tk1.Start(); /*对于安排好的任务,就算调用Start方法也不会立马启动 此时任务的状态为WaitingToRun*/  
    16.         Console.WriteLine(tk1.Status.ToString());  
    17.   
    18.         /*  创建一个主任务 */  
    19.         Task mainTask = new Task(() =>  
    20.         {  
    21.             SpinWait.SpinUntil(() =>  
    22.             {  
    23.                 return false;  
    24.             }, 30000);  
    25.         });  
    26.             
    27.         /*  将子任务加入到主任务完成之后执行 */  
    28.         Task subTask = mainTask.ContinueWith((t1) =>  
    29.         {  
    30.         });  
    31.         /*  启动主任务 */  
    32.         mainTask.Start();  
    33.         /*  此时子任务状态为 WaitingForActivation */  
    34.         Console.WriteLine(subTask.Status.ToString());  
    35.   
    36.   
    37.         /*  创建一个任务 执行 后 等待一段时间 并行未结束的情况下 状态为 Running */  
    38.         Task tk2 = new Task(() =>  
    39.         {  
    40.             SpinWait.SpinUntil(() => false, 30000);  
    41.         });  
    42.         tk2.Start(); /*对于安排好的任务,就算调用Start方法也不会立马启动*/  
    43.         SpinWait.SpinUntil(() => false, 300);  
    44.         Console.WriteLine(tk2.Status.ToString());  
    45.   
    46.   
    47.         /*  创建一个任务 然后取消该任务 状态为Canceled */  
    48.         CancellationTokenSource cts = new CancellationTokenSource();  
    49.         Task tk3 = new Task(() =>  
    50.         {  
    51.             for (int i = 0; i < int.MaxValue; i++)  
    52.             {  
    53.                 if (!cts.Token.IsCancellationRequested)  
    54.                 {  
    55.                     cts.Token.ThrowIfCancellationRequested();  
    56.                 }  
    57.             }  
    58.         }, cts.Token);  
    59.         tk3.Start(); /*启动任务*/  
    60.         SpinWait.SpinUntil(() => false, 100);  
    61.         cts.Cancel(); /*取消该任务执行 但并非立马取消 所以对于Canceled状态也不会立马生效*/  
    62.         SpinWait.SpinUntil(() => false, 1000);  
    63.         Console.WriteLine(tk3.Status.ToString() + " " + tk3.IsCanceled);  
    64.         SpinWait.SpinUntil(() => false, 1000);  
    65.         Console.WriteLine(tk3.Status.ToString() + " " + tk3.IsCanceled);  
    66.         SpinWait.SpinUntil(() => false, 1000);  
    67.         Console.WriteLine(tk3.Status.ToString() + " " + tk3.IsCanceled);  
    68.   
    69.         /*创建一个任务 让它成功的运行完成 会得到 RanToCompletion 状态*/  
    70.         Task tk4 = new Task(() =>  
    71.         {  
    72.             SpinWait.SpinUntil(() => false, 10);  
    73.         });  
    74.         tk4.Start();  
    75.         SpinWait.SpinUntil(() => false, 300);  
    76.         Console.WriteLine(tk4.Status.ToString());  
    77.   
    78.         /*创建一个任务 让它运行失败 会得到 Faulted 状态*/  
    79.         Task tk5 = new Task(() =>  
    80.         {  
    81.             throw new Exception();  
    82.         });  
    83.         tk5.Start();  
    84.         SpinWait.SpinUntil(() => false, 300);  
    85.         Console.WriteLine(tk5.Status.ToString());  
    86.   
    87.         Console.ReadLine();  
    88.     }  
    89. }  
    90.   
    91. class Product  
    92. {  
    93.     public string Name { getset; }  
    94.     public string Category { getset; }  
    95.     public int SellPrice { getset; }  
    96. }  
     
     

    18.1.1 ContinueWith()

    Task 包含一个ContinueWith()方法,它的作用是将任务链接起来。
     

    18.1.2 task上未处理的异常

    Task执行期间产生的未处理异常会被禁止(suppressed),知道调用某个任务完成成员:Wait()、Result、Task.WaitAll()或者Task.WaitAny()。
     

    18.1.3 取消任务

    .NET4之前没有支持取消请求。相反,只能依赖一种“野蛮”中断方式。
     

    18.1.4 长时间运行任务

    如果开发人员知道一个Task要长时间运行,会长时间“霸占”一个底层线程资源,开发人员应告诉线程池共享线程不会太快交还。线程池更有可能为任务创建一个专用线程(而不是分配其中一个共享线程)。为此,调用StartNew()时,要使用 TaskCreationOptions.LongRunning 选项。
     

    18.1.5 释放一个任务

    当程序开始退出的时候,如果Task仍在运行,Task所依赖的底层线程会被CLR终止。所以,因为线程终止而造成的不好的结果可能会在应用程序退出时发生。首选方案是协作式取消,Task支持取消,而应用程序会调用取消,并等待任务结束。
     

    18.2 并行迭代

     
    18.2.1 使用 System.AggregateException 进行并行异常处理
     
    18.2.2 取消并行循环
     
     
     
     

    18.3 并行执行LINQ查询

     
     
     

    18.4 .NET4.0之前的多线程处理

     
     

    18.5 AppPomain 的未处理异常

     
     
     
     
     
    参考:
     
     





  • 相关阅读:
    Mac下持续集成-查看占用的端口及kill
    windons模拟linux终端工具----cmder
    windows中使用django时报错:A server error occurred. Please contact the administrator.
    Pycharm和Navicat免费全功能版资源(即破解版)
    git常用命令
    pycharm常用的快捷键
    CSS3的媒体查询
    CSS列表中与list-style=none等价的样式
    CSS3中的mouse事件(mouseleave,mouseenter,mouseout,mouseover)
    CSS层级(浮动,定位的层级提升)
  • 原文地址:https://www.cnblogs.com/tangge/p/6210535.html
Copyright © 2011-2022 走看看