zoukankan      html  css  js  c++  java
  • 浅谈Task的用法

    Task是用来实现多线程的类,在以前当版本中已经有了Thread及ThreadPool,为什么还要提出Task类呢,这是因为直接操作Thread及ThreadPool,向线程中传递参数,获取线程的返回值及线程当启停都非常的麻烦,所以微软的工程师对Thread进行了再封装,这就是Task,可以这么说Task是架构在Thread之上的,

    所以多线程时Task是我们的首选。

    Task类和Task<TResult>类,后者是前者的泛型版本。TResult类型为Task所调用方法的返回值。

    主要区别在于Task构造函数接受的参数是Action委托,而Task<TResult>接受的是Func<TResult>委托

    1 Task(Action) 
    2 Task<TResult>(Func<TResult>)  

    一、Task的声明

    Task的声明有两种方式:

    a,通过new 的方式来声明

    1 Task objTask = new Task();

    b.通过Task.Factory.StartNew的方式来声明

    1   Task.Factory.StartNew(MyMethod);

    这两种声明方式的区别,第一种声明方式开启线程必须使用objTask.Start(),而通过Task.Factory.StartNew的方式则不用。

     二、Task常用方法

     a.启动一个任务

    1 static void Main(string[] args) 
    2         { 
    3             Task Task1 = new Task(() => Console.WriteLine("Task1")); 
    4             Task1.Start(); 
    5             Console.ReadLine(); 
    6         } 

    通过实例化一个Task对象,然后Start,这种方式中规中矩,但是实践中,通常采用更方便快捷的方式

    Task.Run(() => Console.WriteLine("Foo"));

    这种方式直接运行了Task,不像上面的方法还需要调用Start();

    Task.Run方法是Task类中的静态方法,接受的参数是委托。返回值是为该Task对象。

    Task.Run(Action)

    Task.Run<TResult>(Func<Task<TResult>>)

    Task构造方法还有一个重载函数如下:

    Task 构造函数 (Action, TaskCreationOptions),对应的Task泛型版本也有类似构造函数。TaskCreationOptions参数指示Task创建和执行的可选行为。常用的参数LongRunning。

    b.任务等待

    默认情况下,Task任务由线程池异步执行,想要知道Task任务是否完成,可以通过Task.IsComplated属性获得

    也可以使用Task.Wait()方法来等待线程的完成,Task.Wait()方法会阻塞当前线程。

     1 static void Main(string[] args) 
     2         { 
     3             Task Task1=Task.Run(() => { Thread.Sleep(5000); 
     4             Console.WriteLine("Foo"); 
     5                 Thread.Sleep(5000); 
     6             }); 
     7             Console.WriteLine(Task1.IsCompleted); 
     8             Task1.Wait();//阻塞当前线程 
     9             Console.WriteLine(Task1.IsCompleted); 
    10         }

    还需要说的是,Wait方法有个重构方法,签名如下:public bool Wait(int millisecondsTimeout),接受一个时间。如果在设定时间内完成就返回true,否则返回false。如下的代码:

     1 static void Main(string[] args) 
     2         { 
     3             Task Task1=Task.Run(() => { Thread.Sleep(5000); 
     4             Console.WriteLine("Foo"); 
     5                 Thread.Sleep(5000); 
     6             }); 
     7  
     8             Console.WriteLine("Task1.IsCompleted:{0}",Task1.IsCompleted); 
     9             bool b=Task1.Wait(2000); 
    10             Console.WriteLine("Task1.IsCompleted:{0}", Task1.IsCompleted); 
    11             Console.WriteLine(b); 
    12             Thread.Sleep(9000); 
    13             Console.WriteLine("Task1.IsCompleted:{0}", Task1.IsCompleted); 
    14        }

    输出的结果为:

    C.获取返回值

    要获得返回值,就要用到Task的泛型版本了。 说到Task的返回值就不得不说await和async关键字了。

    当函数使用async标记后,返回值必须为void,Task,Task<T>,当返回值为Task<T>时,函数内部只需要返回T类型,编译器会自动包装成Task<T>类型

    await关键字必须在具有async标记的函数内使用。举个例子:

     1 static void Main(string[] args)
     2         {
     3             Console.WriteLine("调用主线程");
     4             Task<string> s = Testasync();
     5             Console.WriteLine(s.Result);
     6             Console.ReadKey();
     7 
     8         }
     9          static async Task<string> Testasync()
    10         {
    11             Console.WriteLine("运行Task之前" + Thread.CurrentThread.ManagedThreadId);
    12 
    13             Task<string> t= Task.Run<string>(() => 
    14             {
    15                 Console.WriteLine("运行Task" + Thread.CurrentThread.ManagedThreadId);
    16                 Thread.Sleep(9000);
    17                 return "我是测试线程";
    18             });
    19             Console.WriteLine("运行Task之后" + Thread.CurrentThread.ManagedThreadId);
    20             var result = await t;
    21             return result;
    22         }

    从运行结果可以看出:

    1)在async标识的方法体里面,如果没有await关键字的出现,那么这种方法和调用普通的方法没什么区别(就是说async和await是成对出现的,没有await的async是没有意义的)

    (2)在async标识的方法体里面,在await关键字出现之前,还是主线程顺序调用的,直到await关键字的出现才会出现线程阻塞。

    (3)await关键字可以理解为等待方法执行完毕,除了可以标记有async关键字的方法外,还能标记Task对象,表示等待该线程执行完毕。所以await关键字并不是针对于async的方法,而是针对async方法所返回给我们的Task。

    D.延续任务

    所谓延续任务,就是说在任务执行完成之后继续执行任务,有两种方法

    第一种,使用一种是使用GetAwaiter方法。GetAwaiter方法返回一个TaskAwaiter结构,该结构有一个OnCompleted事件,只需对OnCompleted事件赋值,即可在完成后调用该事件。 

     1 static void Main(string[] args) 
     2         { 
     3             Task<int> Task1 = Task.Run<int>(() => { return Enumerable.Range(1, 100).Sum(); }); 
     4             var awaiter = Task1.GetAwaiter(); 
     5             awaiter.OnCompleted(() => 
     6             { 
     7                 Console.WriteLine("Task1 finished"); 
     8                 int result = awaiter.GetResult(); 
     9                 Console.WriteLine(result); // Writes result 
    10             }); 
    11             Thread.Sleep(1000); 
    12         }

    第二种,使用ContinueWith方法

    ContinueWith返回的任然是一个Task类型。ContinueWith方法有很多重载,算上泛型版本,差不多40个左右的。其中最常用的,就是接受一个Action或者Func委托,而且,这些委托的第一个传入参数都是Task类型,即可以访问先前的Task对象。示例: 

    1 static void Main(string[] args) 
    2         { 
    3             Task<int> Task1 = Task.Run<int>(() => {return Enumerable.Range(1, 100).Sum(); }); 
    4             Task1.ContinueWith(antecedent => { 
    5             Console.WriteLine(antecedent.Result); 
    6             Console.WriteLine("Runing Continue Task"); 
    7 }); 
    8             Thread.Sleep(1000); 
    9         }

    E,延迟任务

    Task.Delay()方法是相当于异步的Thread.Sleep();

  • 相关阅读:
    别让你的生活止于平庸!(摘)
    NSURLSession 请求
    第三方原理
    iOS实用的小技巧
    简书APP
    网络请求
    JQuery 简介
    struts2拦截器的实现原理及源码剖析
    hibernate配置文件注意点
    hibernate中三种状态
  • 原文地址:https://www.cnblogs.com/Artist007/p/11538437.html
Copyright © 2011-2022 走看看