C#多线程编程笔记(5.3)-对连续的异步任务使用await操作符

近来在学习Eugene Agafonov编写的《C#多线程编程实战》(译),做些笔记也顺便分享一下^-^

本篇将展示当代码中有多个连接的await方法时程序的实际流程是怎样的。我们将学习如何阅读有await方法的代码,以及理解为什么await调用是异步操作。

using System;
using System.Threading.Tasks;
using System.Threading;

namespace 对连续的异步任务使用await操作符
{
    class Program
    {
        static void Main(string[] args)
        {
            Task t = AsynchronyWithTPL();
            t.Wait();

            t = AsynchronyWithAwait();
            t.Wait();

            Console.ReadKey();
        }

        static Task AsynchronyWithTPL()
        {
            var containerTask = new Task(() =>
              {
                  Task<string> t = GetInfoAsync("TPL 1");
                  t.ContinueWith(task =>
                  {
                      Console.WriteLine(t.Result);
                      Task<string> t2 = GetInfoAsync("TPL 2");
                      t2.ContinueWith(innerTask =>
                      {
                          Console.WriteLine(innerTask.Result);
                      }, TaskContinuationOptions.NotOnFaulted | TaskContinuationOptions.AttachedToParent);
                      t2.ContinueWith(innerTask =>
                      {
                          Console.WriteLine(innerTask.Exception.InnerException);
                      }, TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.AttachedToParent);
                  },
                  TaskContinuationOptions.NotOnFaulted | TaskContinuationOptions.AttachedToParent);

                  t.ContinueWith(task =>
                  {
                      Console.WriteLine(t.Exception.InnerException);
                  }, TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.AttachedToParent);
              });

            containerTask.Start();
            return containerTask;
        }

        async static Task AsynchronyWithAwait()
        {
            try
            {
                string result = await GetInfoAsync("Async 1");
                Console.WriteLine(result);
                result = await GetInfoAsync("Async 2");
                Console.WriteLine(result);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }

        async static Task<string> GetInfoAsync(string name)
        {
            Console.WriteLine("Task {0} started!", name);
            await Task.Delay(TimeSpan.FromSeconds(2));
            if (name == "TPL 2")
            {
                throw new Exception("Boom!");
            }
            return string.Format("Task {0} is running on a thread id {1}.Is thread pool thread: {2}",
                name, Thread.CurrentThread.ManagedThreadId,
                Thread.CurrentThread.IsThreadPoolThread);
        }
    }
}

程序运行的结果如下:


当程序运行时,运行了两个异步操作。我们看到AsynchronyWithAwait方法讲起。它看起来仍然像平常的同步代码,唯一不同只处是使用了两个await声明。最重要的一点是该代码依然是顺序执行的,Async 2任务只有等之前的任务完成后才会开始执行。当阅读上述代码时,程序流很清晰,可以看到什么先运行,什么后运行。但该程序如何是异步程序呢?首先,它不总是异步的。当使用await时如果一个任务已经完成,我们会异步地得到该任务结果。否则,当在代码中看到await声明时,通常的行为是方法执行到该await代码行时将立即返回,并且剩下的代码将会在一个后续操作任务中运行。因此等待操作结果时并没有阻塞程序执行,这是一个异步调用。当AsynchronyWithAwait方法中的代码在执行是,除了在Main方法中调用t.Wait外,我们可以执行任何其他任务。然而,主线程必须等待直到所有异步操作完成,否则主线程完成后所有运行异步操作的后台线程会停止运行。

AsynchronyWithTPL方法模仿了AsynchronyWithAwait的程序流。我们需要一个容器任务来处理所有相互依赖的任务。然后启动任务,给其加了一组后续操作。当该任务完成后,会打印出其结果。然后又启动一个任务,在该任务完成后依次运行更多的后续操作。为了测试对异常的处理,当运行第二个任务时故意抛出一个异常,并打印出异常信息。这组后续操作创建了与第一个方法中一样的程序流。如果用它与await方法比较,可以看到它更容易阅读和理解。唯一的技巧是请记住异步并不总是意味着并行执行。

猜你喜欢

转载自blog.csdn.net/qq_35445058/article/details/80914387
今日推荐