在lambda表达式中使用await操作符

本节将展示如何在lambda表达式中使用await。我们将编写一个使用了await的匿名方
法,并且获取异步执行该方法的结果。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace 在lambda表达式中使用await操作符
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Task t = AsynchronousProcessing();
            t.Wait();
        }

        async static Task AsynchronousProcessing()
        {
            Func<string, Task<string>> asyncLambda = async name =>
            {
                await Task.Delay(TimeSpan.FromSeconds(2));
                return string.Format("Task {0} is running on a thread id {1}.Is thread pool thread:{2}", name,
                    Thread.CurrentThread.ManagedThreadId,
                    Thread.CurrentThread.IsThreadPoolThread);
            };

            string result = await asyncLambda("asyn lambda");

            Console.WriteLine(result);
        }
    }
}

运行结果

Task asyn lambda is running on a thread id 4.Is thread pool thread:True

工作原理

首先,由于不能在Main方法中使用async,我们将异步函数移到了Asynchronous-
Processing方法中。然后使用async关键字声明了一个lambda表达式。由于任何lambda表达
式的类型都不能通过lambda自身来推断,所以不得不显式向C#编译器指定它的类型。在本
例中,该类型说明该[ambda表达式接受一个字符串参数,并返回一个Task对象。

接着,我们定义了lambda表达式体。有个问题是该方法被定义为返回一个Task
对象,但实际上返回的是字符串,却没有编译错误!这是因为C#编译器自动产生一个任务
并返回给我们。

最后一步是等待异步lambda表达式执行并打印出结果。

对连续的异步任务使用await操作符

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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Dynamic;

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

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

        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.OnlyOnCanceled | 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.Write("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}",
                Thread.CurrentThread.ManagedThreadId,
                Thread.CurrentThread.IsThreadPoolThread);
        }
    }
}

运行结果

Task {0} started!,nameSystem.FormatException: 索引(从零开始)必须大于或等于零,且小于参数列表的大小。
   在 System.Text.StringBuilder.AppendFormatHelper(IFormatProvider provider, String format, ParamsArray args)
   在 System.String.FormatHelper(IFormatProvider provider, String format, ParamsArray args)
   在 System.String.Format(String format, Object arg0, Object arg1)
   在 对连续的异步任务使用await操作符.Program.<GetInfoAsync>d__3.MoveNext() 位置 G:\C# 多线程编程实战\使用C#5.0\对连续的异步任务使用await操作符\Program.cs:行号 73
Task {0} started!,nameSystem.FormatException: 索引(从零开始)必须大于或等于零,且小于参数列表的大小。
   在 System.Text.StringBuilder.AppendFormatHelper(IFormatProvider provider, String format, ParamsArray args)
   在 System.String.FormatHelper(IFormatProvider provider, String format, ParamsArray args)
   在 System.String.Format(String format, Object arg0, Object arg1)
   在 对连续的异步任务使用await操作符.Program.<GetInfoAsync>d__3.MoveNext() 位置 G:\C# 多线程编程实战\使用C#5.0\对连续的异步任务使用await操作符\Program.cs:行号 73
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
   在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   在 对连续的异步任务使用await操作符.Program.<AsynchronyWithAwait>d__2.MoveNext() 位置 G:\C# 多线程编程实战\使用C#5.0\ 对连续的异步任务使用await操作符\Program.cs:行号 56

工作原理

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

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

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐