zoukankan      html  css  js  c++  java
  • .NET异步编程之Task

      一.FrameWork 4.0之前的线程世界    

      在.NET FrameWork 4.0之前,如果我们使用线程。一般有以下几种方式:

      1.使用System.Threading.Thread 类,调用实例方法Start()开启一个新线程,调用Abort()方法来提前终止线程。

      2.使用System.Threading.ThreadPool类,调用静态方法QueueUserWorkItem(),将方法放入线程池队列,线程池来控制调用。

      3.使用BeginInvoke,EndInvoke,BeginRead,EnRead,BeginWrite,EndWrite等一系列的异步方法。

      4.使用System.ComponentModel.BackgroundWorker控件,调用实例方法RunWorkerAsync(),开启一个新线程。 

      二.创建一个新线程时会产出哪些开销

      1.线程内核对象,包含一组对线程进行描述的属性。

       2.线程环境块,包含线程异常处理的链首,线程进入的每个try{}块会在链首插入一个节点,从try{}块推出时,会在链首删除该节点。

       3.用户模式栈,用来存储传给方法的局部变量和实参,它还包含一个地址,指出当前方法返回知,该从什么地方继续执行。

       4.内核模式栈,应用程序代码向操作系统中的一个内核模式的函数传递实参时,会使用内核模式栈。

       5.DLL线程附加和线程分离通知,线程开启或者终止时,会调用进程中加载的所有DLL的DLLMain方法。

      三.线程池

       由于创建一个新的线程是一个昂贵的操作,所以有了线程池来维护了一个线程队列,例如常见的数据库连接池,IIS连接池等。线程池在FrameWork 4.0之前,我们可以使用ThreadPool.QueueUserWorkItem()将一个符合WaitHandle委托类型的方法加入到线程池队列中。

    代码如下:

     1  static void Main(string[] args)
     2         {
     3             Console.WriteLine("Main");
     4             ThreadPool.QueueUserWorkItem((o) =>
     5             {
     6                 Console.WriteLine(DateTime.Now);
     7             });
     8             Thread.Sleep(1000);
     9             Console.WriteLine("Main Next...");
    10             Console.Read();
    11         }

    为了验证线程池内的方法确实是异步的,我们在Main方法中让主线程停止1秒。测试结果确实是线程池内的方法和Main方法是异步执行的。

      四.取消操作

       FrameWork提供了一个取消操作的模式,这就意味着我们可以取消正在执行的操作,对于耗时的操作来说,是非常好的用户体验。为了取消一个操作,要创建一个System.Threading.CancellationTokenSource类的实例。(MSDN入口http://msdn.microsoft.com/zh-cn/library/vstudio/system.threading.cancellationtokensource.aspx

    )。这个对象包含了管理和取消的所有状态,Token属性可以获取CancellationToken的实例,可以根据IsCancellationRequested属性来判断是否需要取消操作。同时,可以通Register方法注册一个在取消时调用的委托。

    代码如下:

     1  static void Main(string[] args)
     2         {
     3             CancellationTokenSource cts = new CancellationTokenSource();
     4             cts.Token.Register(() => Console.WriteLine("Register"));
     5             Console.WriteLine("Main");
     6             ThreadPool.QueueUserWorkItem((o) =>
     7             {
     8                 CancellationToken ct = (CancellationToken)o;
     9 
    10                 for (int i = 0; i < 100; i++)
    11                 {
    12                     //是否需要取消操作
    13                     if (ct.IsCancellationRequested)
    14                     {
    15                         break;
    16                     }
    17                     Console.WriteLine(DateTime.Now);
    18                     Thread.Sleep(100);
    19                 }
    20 
    21 
    22             }, cts.Token);
    23             Thread.Sleep(1000);
    24             //取消
    25             cts.Cancel();
    26             Console.WriteLine("Main Next...");
    27             Console.Read();
    28         }

    可以看到在Main方法中创建了CancellationTokenSource的实例,同时注册了一个在取消时调用的委托,并且把这个实例传给了线程池方法。在线程池方法的循环内判断是否需要取消任务,最后在Main方法内调用cts.Cancel()取消了操作。

      五.Task(任务)

      1.创建任务

      调用ThreadPool.QueueUserWorkItem()方法来处理异步的操作是非常简单的。但是这个是有很多限制的。比如,我们不知道线程池什么时候开始执行方法,什么时候方法执行结束,而且也没有方法的返回值。所以在FrameWork 4.0里,引入了Task的概念。我们可以在 System.Threading.Tasks命名空间下找到它们(MSDN入口http://msdn.microsoft.com/zh-cn/library/vstudio/system.threading.tasks.task.aspx),可以用Task做同样的异步操作。

    代码如下:

     1  static void Main(string[] args)
     2         {
     3             Console.WriteLine("Main");
     4             Task<int> task = new Task<int>(() =>
     5             {
     6                 int sum = 0;
     7                 for (int i = 0; i <= 1000; i++)
     8                 {
     9                     Thread.Sleep(10);
    10                     sum += i;
    11                 }
    12                 return sum;
    13             });
    14             task.Start();
    15             Console.WriteLine(task.Result);//获取任务的执行结果
    16             Console.Read();
    17         }

      要注意的是调用task.Result获取返回值,或者是task.Wait()等待任务执行完成,主线程将会被阻塞。要等到Task执行完成才会继续执行。同时,如果Task内部抛出了一个未处理的异常,这个异常会在调用Result或者Wait()是时候会抛出System.AggregateException。

       2.一个任务完成后自动执行一个新任务

      由于调用task.Result或者task.Wait()时会阻塞,所以Task提供了一个ContinueWith()方法,有很多重载。这个方法可以在一个任务完成时,启动一个新任务,并不阻塞主线程。

    代码如下:

     1  static void Main(string[] args)
     2         {
     3             Console.WriteLine("Main");
     4             Task<int> task = new Task<int>(() =>
     5             {
     6                 int sum = 0;
     7                 for (int i = 0; i <= 1000; i++)
     8                 {
     9                     Thread.Sleep(10);
    10                     sum += i;
    11                 }
    12 
    13                 return sum;
    14             });
    15             task.Start();
    16             task.ContinueWith((t) => Console.WriteLine(t.Result));//获取任务的执行结果
    17             Console.WriteLine("Main Next");
    18             Console.Read();
    19         }

      要注意的是,执行到ContinueWith的时候,可能第一个求和的任务已经完成了。不过这不影响结果,ContinueWith方法会立即启动第二个任务。

       3.任务的状态

      Task内部有Status的只读属性,这个的属性是TaskStatus类型的枚举。在Task对象的生存期间,可以通过Status获取任务的的当前状态。这个枚举的状态的定义如下:

     1    public enum TaskStatus
     2     {
     3         Created = 0,                     //该任务已初始化,但尚未被计划
     4 
     5         WaitingForActivation = 1,        //该任务正在等待 .NET Framework 基础结构在内部将其激活并进行计划。
     6 
     7         WaitingToRun = 2,                //该任务已被计划执行,但尚未开始执行。
     8 
     9         Running = 3,                     //该任务正在运行,但尚未完成。
    10 
    11         WaitingForChildrenToComplete = 4,//该任务已完成执行,正在隐式等待附加的子任务完成。  
    12    
    13         RanToCompletion = 5,             //已成功完成执行的任务。  
    14 
    15         Canceled = 6,                    //该任务已通过对其自身的 CancellationToken 引发 OperationCanceledException 对取消进行了确认,
    16                                          // 此时该标记处于已发送信号状态;或者在该任务开始执行之前,已向该任务的CancellationToken 发出了信号。  
    17 
    18         Faulted = 7,                     // 由于未处理异常的原因而完成的任务。  
    19     }

      在创建一个Task对象时,状态是Created。当调用Start()方法,任务启动时,状态变成了WaitingToRun(),任务真正开始执行时,状态变成了Running,任务结束时,对应的三种不同的状态:成功、被取消、执行中出现未处理异常,分别对应:RanToCompletion、Canceled、Faulted。

            

  • 相关阅读:
    08-30 作业整理
    JS题目整理
    08-20作业整理
    CSS3知识总结
    CSS样式表知识总结
    html标签知识总结
    Dice (III) 概率dp
    Island of Survival 概率
    How far away ?
    Hdu 2874 Connections between cities
  • 原文地址:https://www.cnblogs.com/LoveJerryZhang/p/2821053.html
Copyright © 2011-2022 走看看