.NET(C#):await返回Task的async方法

一.  FrameWork 4.0以前的线程世界    编程

    在.NET FrameWork 4.0以前,若是咱们使用线程。通常有如下几种方式:多线程

  • 使用System.Threading.Thread 类,调用实例方法Start()开启一个新线程,调用Abort()方法来提早终止线程。
  • 使用System.Threading.ThreadPool类,调用静态方法QueueUserWorkItem(),将方法放入线程池队列,线程池来控制调用。
  • 使用BeginInvoke,EndInvoke,BeginRead,EnRead,BeginWrite,EndWrite等一系列的异步方法。
  • 使用System.ComponentModel.BackgroundWorker控件,调用实例方法RunWorkerAsync(),开启一个新线程。 

二.  .Net 传统异步编程概述 app

  • 异步编程模型 (APM),在该模型中异步操做由一对 Begin/End 方法(如 FileStream.BeginRead 和 Stream.EndRead)表示。
  • 基于事件的异步模式 (EAP),在该模式中异步操做由名为“操做名称Async”和“操做名称Completed”的方法/事件对(例如 WebClient.DownloadStringAsync 和 WebClient.DownloadStringCompleted)表示。 (EAP 是在 .NET Framework 2.0 版中引入的,在silverlight或者wpf变成中常常用到)。

三.  Task 的优势以及功能   异步

  • 在任务启动后,能够随时以任务延续的形式注册回调。
  • 经过使用 ContinueWhenAll 和 ContinueWhenAny 方法或者 WaitAll 方法或 WaitAny 方法,协调多个为了响应 Begin_ 方法而执行的操做。
  • 在同一 Task 对象中封装异步 I/O 绑定和计算绑定操做。
  • 监视 Task 对象的状态。
  • 使用 TaskCompletionSource 将操做的状态封送到 Task 对象。

 

众所周知,async方法只能够返回void,Task和Task<T>。async

 

对于返回void的async方法,它并非awaitable,因此其余方法不能用await方法来调用它,而返回Task的async方法则能够。ide

 

那么当async方法返回Task后,接着await,那被await的Task是一个什么概念?是async方法中第一个被await的Task?不,它表明目标async方法的所有执行,其中包括被await分割的链接Task,可是不包括非await形成的多线程执行。异步编程

 

以下代码,在doo是一个返回Task的async方法,而后在另外一个方法test中await调用doo,而后在Main方法中调用test(因为Main方法不容许加async,因此须要另外加一个async方法来使用await)测试

static void Main(string[] args)this

{线程

    test();

    log("Main:调用test后");

    Thread.Sleep(Timeout.Infinite);

}

 

//Main方法不容许加async,因此咱们用这个方法使用await

static async void test()

{

    log("test: await以前");

    await doo();

    log("test: await以后");

}

 

//返回Task的async方法

static async Task doo()

{

    log("doo: Task结果:" + await Task.Run(() => { Thread.Sleep(1000); log("Task"); return 1; }));

    log("doo: Task结果:" + await Task.Run(() => { Thread.Sleep(1000); log("Task"); return 2; }));

    log("doo: Task结果:" + await Task.Run(() => { Thread.Sleep(1000); log("Task"); return 3; }));

    Thread.Sleep(1000);

    Console.WriteLine("doo中在Task外的Thread.Sleep执行完毕");

}

 

//输出方法:显示当前线程的ManagedThreadId

static void log(string msg)

{

    Console.WriteLine("{0}: {1}", Thread.CurrentThread.ManagedThreadId, msg);

}

 

上面代码会输出:

1: test: await以前

1: Main:调用test后

3: Task

3: doo: Task结果:1

4: Task

4: doo: Task结果:2

3: Task

3: doo: Task结果:3

doo中在Task外的Thread.Sleep执行完毕

3: test: await以后

 

前两句简单,调用test方法,await后的内容会被加在目标Task的后面,而后test立刻返回,因而输出“Main:调用test后”,同时他们都是在主线程中执行的,因此ManagedThreadId都是1。

 

接着后面就是另外一个Task的执行(固然在另外一个线程,也是test方法中await的目标Task)。这个所谓的Task就是doo方法的所有执行。因此doo中三个顺序执行的Task(经过await一个一个链接)依次执行,因此Task输出结果1,2,3。第一个Task的ManagedThreadId是3,第二个是4,第三个又是3,缘由是Task的内部执行使用了CLR的线程池,因此线程获得了重复利用。

 

接着doo方法尚未完,最后一个await形成doo方法后面的代码在这个await针对的Task执行后继续执行,因而输出:doo中Task外的Thread.Sleep执行完毕。

 

最后当doo完全执行完test的await才结束,因此最后一行输出:test:await以后。

 

 

上面我说过:被await的async方法返回的Task表明“目标async方法的所有执行,其中包括被await分割的链接Task,可是不包括非await形成的多线程执行”。

因此若是把返回Task的async方法(也就是上例中的doo方法)改为这样:

//返回Task的async方法

static async Task doo()

{

    log("doo: Task结果:" + await Task.Run(() => { Thread.Sleep(1000); log("Task"); return 1; }));

    log("doo: Task结果:" + await Task.Run(() => { Thread.Sleep(1000); log("Task"); return 2; }));

    log("doo: Task结果:" + await Task.Run(() => { Thread.Sleep(1000); log("Task"); return 3; }));

 

    //不使用await:线程池多线程

    ThreadPool.QueueUserWorkItem(_ =>

        {

            Thread.Sleep(1000);

            Console.WriteLine("ThreadPool.QueueUserWorkItem");

        });

 

    //不使用await:Task多线程

    Task.Run(() =>

        {

            Thread.Sleep(1000);

            Console.WriteLine("Task.Run");

        });

}

 

咱们加入了不用await的多线程执行,分别使用ThreadPool和Task,整个程序会输出这样的结果:

1: test: await以前

1: Main:调用test后

3: Task

3: doo: Task结果:1

4: Task

4: doo: Task结果:2

3: Task

3: doo: Task结果:3

3: test: await以后

Task.Run

ThreadPool.QueueUserWorkItem

 

不使用await的多线程彻底脱离了test方法中await的Task,是运行在test的await以后的。

 

另外Visual Studio会对Task.Run代码作以下警告:
加载中...


 

提示:Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the ‘await’ operator to the result of the call.

就是说,若是不加await,当前方法会继续执行直到结束,不用管他,由于咱们如今就是在作在async方法中不用await的测试,呵呵。

 

 

或许你会问,为何要用这样的方式去await另外一个async方法返回的Task呢?咱们一直在讨论返回Task的async方法,我认为看一个返回Task<T>的async方法能够更好地解释这个问题。

 

下面咱们把上面的代码改为类似的返回Task<int>的async方法执行,那么doo方法返回Task<T>,他把本身方法内3个awaited Task的结果统一相加,最后返回结果并做为本身返回的Task的结果。而后在test方法中输出doo返回的结果。

 

完整代码:

static void Main(string[] args)

{

    test();

    log("Main:调用test后");

    Thread.Sleep(Timeout.Infinite);

}

 

//Main方法不容许加async,因此咱们用这个方法使用await

static async void test()

{

    log("test: await以前");

    Console.WriteLine("doo结果:{0}", await doo());

    log("test: await以后");

}

 

//返回Task的async方法

static async Task<int> doo()

{

    var res1 = await Task.Run(() => { Thread.Sleep(1000); log("awaited Task1执行"); return

    var res2 = await Task.Run(() => { Thread.Sleep(1000); log("awaited Task2执行"); return

    var res3 = await Task.Run(() => { Thread.Sleep(1000); log("awaited Task3执行"); return

 

    //不使用await:线程池多线程

    ThreadPool.QueueUserWorkItem(_ =>

        {

            Thread.Sleep(1000);

            Console.WriteLine("ThreadPool.QueueUserWorkItem");

        });

 

    //不使用await:Task多线程

    Task.Run(() =>

        {

            Thread.Sleep(1000);

            Console.WriteLine("Task.Run");

        });

 

    return res1 + res2 + res3;

}

 

//输出方法:显示当前线程的ManagedThreadId

static void log(string msg)

{

    Console.WriteLine("{0}: {1}", Thread.CurrentThread.ManagedThreadId, msg);

}

 

先看结果:

1: test: await以前

1: Main:调用test后

3: awaited Task1执行

4: awaited Task2执行

4: awaited Task3执行

doo结果:6

4: test: await以后

ThreadPool.QueueUserWorkItem

Task.Run

 

 

和上一个返回Task的例子同样,当在test方法中await doo方法返回的Task,doo内awaited Task都被先等了,而没有awaited的线程都并无被等,这是为何呢(也就是上面留下的那个问题)?下面用这个返回Task<int>的例子解释一下:

在test中await doo返回的Task,那么此时咱们须要他的结果,而他的结果是须要本身方法内所包含的其余awaited结果,能够理解成被等的子结果。因此本身的结果须要其余的结果,那么等这个结果必须须要等那些被依赖的结果也出来。因此test方法await doo方法的结果会一样等待全部doo内的await,不会管其余doo内非await的多线程执行(固然从技术角度讲,也是不可能的,由于async/await能够这样全靠的是编译器)。

摘自 Mgen

相关文章
相关标签/搜索