• Task异步编程


     

    Task异步编程中,可以实现在等待耗时任务的同时,执行不依赖于该耗时任务结果的其他同步任务,提高效率。

    1、Task异步编程方法签名及返回值:

      a) 签名有async 修饰符

      b) 方法名以 Async 结尾(良好的编码习惯)

        根据约定,将“Async”追加到具有 async 修饰符的方法名称。如果某一约定中的事件、基类或接口协定建议其他名称,则可以忽略此约定。例如,你不应重命名常用事件处理程序,例如 btnOpen_Click。

      c) 返回类型如下:

    • 如果方法有操作数为TResult 类型的返回语句,则为 Task<TResult>;
    • 如果你的方法没有返回语句或具有没有操作数的返回语句,则为 Task;
    • 如果你编写的是异步事件处理程序,则为 void。

      d) 方法通常包含至少一个 await 表达式,该表达式标记一个点,在该点上,直到等待的异步操作完成,方法才能继续。 同时,将方法挂起,并且控制权将返回到方法的调用方。

    举例如下:

    private static async Task<int> OperateAsync()

    2、 举例分析:

    static void Main(string[] args)
    {
        Test1.DoClickAsync();
        Console.WriteLine("不等待异步方法,直接执行");
        Console.ReadKey();
    }
    public class Test1
    {
        public static async void DoClickAsync()
        {
            Task<int> result = OperateAsync();
            Console.WriteLine("从OperateAsync方法中控制权限返回调用方DoClickAsync");
            int resultValue = await result;
            Console.WriteLine("耗时操作结果" + resultValue.ToString());
        }
    
        private static async Task<int> OperateAsync()
        {
            HttpClient client = new HttpClient();
    
            //执行异步方法 GetStringAsync
            Task<string> getStringTask = client.GetStringAsync("https://msdn.microsoft.com");
    
            //由于下面的非异步方法不依赖上面异步方法结果,因此可以先执行,假设在这里执行一些非异步的操作
            DoIndependentWork();
    
            //等待操作挂起方法 OperateAsync,直到 getStringTask 完成,OperateAsync 方法才会继续执行
            //同时,控制将返回到 OperateAsync 方法的调用方,直到 getStringTask 完成后,将在这里恢复控制。
            //然后从 getStringTask 拿到字符串结果
            string urlContents = await getStringTask;
    
            Console.WriteLine("方法OperateAsync");
            //返回字符串的长度(int 类型)
            return urlContents.Length;
    
        }
    
        private static void DoIndependentWork()
        {
            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine("主线程执行内容" + i);
                Thread.Sleep(1000);
            }
        }
    }

    3、步骤分析:

    • Main主函数,调用并等待异步方法DoClickAsync;
    • DoClickAsync方法调用并等待异步方法OperateAsync;
    • 异步方法OperateAsync调用HttpClient对象并调用它的GetStringAsync异步方法来下载网站内容
    • 假设GetStringAsync中发生了某种情况,该情况挂起了它的进程。可能必须等待网站下载或一些其他阻塞的活动。为避免阻塞资源,GetStringAsync 会将控制权出让给其调用方 OperateAsync。GetStringAsync返回Task,其中TResult为字符串,并且OperateAsync将任务分配给getStringTask变量。该任务表示调用GetStringAsync的正在进行的进程,其中承诺当工作完成时产生实际字符串值。
    • 由于尚未等待getStringTask,因此,OperateAsync可以继续执行不依赖于 GetStringAsync得出最终结果的其他任务。该任务由对同步方法 DoIndependentWork 的调用表示。
    • DoIndependentWork 是完成其工作并返回其调用方的同步方法。
    • OperateAsync 已完成工作,可以不受 getStringTask 的结果影响。 接下来,OperateAsync 需要计算并返回该下载字符串的长度,但该方法仅在具有字符串时才能计算该值。因此,OperateAsync 使用一个 await 运算符来挂起其进度,并把控制权交给调用 OperateAsync 的方法。OperateAsync将 Task<int> 返回至调用方。该任务表示对产生下载字符串长度的整数结果的一个承诺。

    【备注】

    (1)如果 GetStringAsync(即 getStringTask)在 OperateAsync 等待前完成,则控制权会保留在 OperateAsync 中。 如果异步调用过程 (getStringTask) 已完成,并且OperateAsync不必等待最终结果,则挂起然后返回到 OperateAsync,但这会造成成本的浪费。

    (2)在调用方内部(DoClickAsync),处理模式将继续。在等待结果前,调用方可以开展不依赖于OperateAsync结果的其他工作,否则就需等待片刻。DoClickAsync等待 OperateAsync,而 OperateAsync 等待 GetStringAsync。

    • GetStringAsync 完成并生成一个字符串结果。 字符串结果不是通过你预期的方式调用 GetStringAsync 所返回的。(请记住,此方法已在前面步骤中返回一个任务。)相反,字符串结果存储在表示完成方法 getStringTask 的任务中。 await 运算符从 getStringTask 中检索结果。赋值语句将检索到的结果赋给 urlContents。
    • 当OperateAsync具有字符串结果时,该方法可以计算字符串长度。然后,OperateAsync 工作也将完成,并且等待程序DoClickAsync可继续使用。

    4、同步行为和异步行为差异:

    • 同步行为,当其工作完成时会返回一个同步方法;
    • 异步行为,当其工作挂起时,异步方法会返回一个任务值。在异步方法最终完成其工作时,任务会标记为已完成,而结果(如果有)将存储在任务中。

    5、详谈返回类型:

    在.NET 中,异步方法通常返回 Task 或 Task<TResult>。在异步方法中,await运算符应用于通过调用另一个异步方法返回的任务。

    1、如果方法包含指定类型 TResult 的操作数的return语句,则将 Task<TResult> 指定为返回类型。

    2、如果方法不含任何 return 语句或包含不返回操作数的 return 语句,则将Task用作返回类型。

    【备注】

    每个返回的任务表示正在进行的工作。任务可封装有关异步进程状态的信息,如果未成功,则最后会封装来自进程的最终结果或进程引发的异常。

    3、异步方法还可以是具有void返回类型。该返回类型主要用于定义需要void返回类型的事件处理程序。异步事件处理程序通常用作异步程序的起始点。无法等待具有void返回类型的异步方法,并且一个void返回值的调用方无法捕获该方法引发的任何异常。

    下面的示例演示如何声明并调用可返回 Task 或 Task<TResult> 的方法。

            static void Main(string[] args)
            {
                DoMethodAsync();
                Console.ReadKey();
            }
    
            private static async void DoMethodAsync()
            {
                //调用Method1Async
                /*方式一*/
                Task<Guid> t1 = Method1Async();
                Guid guid1 = t1.Result;
                Console.WriteLine("调用Method1Async方式一操作结果" + guid1.ToString());
                /*方式二*/
                Guid guid2 = await Method1Async();
                Console.WriteLine("调用Method1Async方式二操作结果" + guid2.ToString());
    
                //调用Method2Async
                /*方式一*/
                Task t2 = Method2Async();
                await t2;
                /*方式二*/
                await Method2Async();
            }
    
            //返回Task<TResult>类型
            static async Task<Guid> Method1Async()  //Task<Guid>
            {
                Console.WriteLine("调用Method1Async");
                var result = Guid.NewGuid();
    
                await Task.Delay(10000);
    
                //这里返回一个 Guid 的类型
                return result;
            }
    
            //返回Task类型
            static async Task Method2Async()  //Task
            {
                Console.WriteLine("调用Method2Async");
                //Do...
    
                await Task.Delay(10000);
    
                //Do...
    
                //这里没有 return 语句
            }

    6、总结:

    1、标记的异步方法(通过使用 async 修饰符)可以使用 await 来指定悬挂点。await 运算符通知编译器,异步方法只有直到等待的异步过程完成才能继续通过该点。同时,控制权将返回至异步方法的调用方。

    2、异步方法通常包含 await 运算符的一个或多个匹配项,但缺少 await 表达式不会导致编译器错误。如果异步方法未使用 await 运算符标记悬挂点,则该方法将作为同步方法执行,不管异步修饰符(async)如何。编译器将为此类方法发布一个警告。

    7、参考:

    http://www.cnblogs.com/liqingwen/p/5922573.html

  • 相关阅读:
    nginx学习二:快速入门
    nginx学习一:http协议
    使用itext生成pdf的,各种布局
    关于java poi itext生成pdf文件的例子以及方法
    半透明全屏蒙层+全屏屏蔽+内容居中+css
    通过html文件生成PDF文件
    mybatis中文官网
    经典sql语句
    关于Cell中的各种值的类型判断
    bootstrap表格参数说明
  • 原文地址:https://www.cnblogs.com/SimplePerson/p/6103839.html
Copyright © 2020-2023  润新知