zoukankan      html  css  js  c++  java
  • C#中的异步多线程3 await和异步控制流

    异步方法的结构包含有3个不同的区域:

    async Task<int> CountCharactersAsync(int id,string site)
    {
        //section 1 第一个await表达式之前的部分
        Console.WriteLine("Starting CounCharacters");
        WebClient wc=new WebClient();
        //section 2 await表达式
        string result=await wc.DownloadStringTaskAsync(new Uri(site));
        //section 3 后续部分
        Console.WriteLine("CountCharacters Completed");
        return result.Length;
    }

    section 1:从方法开头到第一个await表达式之前的所有代码,这一部分应只包含少量且无需长时间处理的代码。

    section 2:await表达式,表示将被异步执行的任务。

    section 3:后续部分,await表达式之后出现的方法中的其余代码。包括其执行环节,所在线程信息,目前作用域内的变量值,以及当await表达式完成后要重新执行所需的其他信息。

    一、异步方法控制流

           一个异步方法的控制流,从第一个await表达式之前的代码开始,同步执行,直到遇到第一个await。这一区域实际上在第一个await表达式处结束,此时await任务还没有完成(一般没有完成)。当await任务完成时,方法将继续同步执行。如果还有其他await,就重复上述过程。(异步方法内部)

           当到达await表达式时,异步方法将控制返回到调用方法(跳出异步方法至调用方法)。如果方法的返回类型为Task或Task<T>类型,就讲创建一个Task对象,表示需异步完成的任务和后续,然后将该Task返回到调用方法。 

           异步方法内部:

           1、异步执行await表达式的空闲任务

           2、当await表达式完成时,执行后续部分。后续部分本身也可能包含其他await表达式,这些表达式也将按照相同方式处理,即异步执行await表达式,然后执行后续部分。

           3、当后续部分遇到return语句或到达方法末尾时,将:

                 3-1、如果方法返回类型为void,控制流退出

                 3-2、如果方法返回类型为Task,后续部分设置Task的属性并退出。如果返回类型为Task<T>,后续部分还将设置Task对象的Result属性。

           同时,调用方法中的代码将继续其进程,从异步方法获取Task对象。当需要实际值时,就引用Task对象的Result属性。届时,如果异步方法设置了该属性,调用方法就能获得该值并继续。否则,将暂停并等待该属性被设置,然后再继续执行。

            具体的各种控制流

            1、调用方法调用异步方法——同步执行代码,没有await表达式-返回调用方法继续执行

            2、调用方法调用异步方法——同步执行代码,有await表达式——await表达式任务没有完成——创建空闲任务,创建后续部分,返回到调用方法——A、返回到调用方法的部分会继续执行。B、异步方法会执行await的空闲任务——执行后续部分——设置返回的Task状态和返回值——退出

            3、调用方法调用异步方法——同步执行代码,有await表达式——await表达式任务完成——同步执行代码,有await表达式.

            注意:同步方法第一次遇到await时,所返回的对象类型并非await表达式的返回类型,而是同步方法头中的返回类型,它与await表达式的返回值类型没有任何关系。

            例如:  

    private async Task<int> CountCharactersAsync(string site)
    {
       WebClient wc=new WebClient();
       string result=await wc.DownloadStringTaskAsync(new Uri(site));
       return result.Length;
    }

             同步方法第一次遇到await表达式时所返回的对象其实是Task<int>,而并不是await表达式的string。Task<int>是异步方法的返回类型。而实际上return语句“返回”一个结果或到达异步方法结尾时,它并没有返回真正的某个值,而只是单纯的退出了。

    二、await表达式

            await表达式指定了一个异步执行的任务。其语法由await关键字和一个空闲对象/任务(Task)组成。这个任务可能是一个Task类型的对象,也可能不是。默认情况下,这个任务在当前线程异步执行。

            一个空闲对象/任务即是一个awaitable类型的实例。awaitable类型是指包含GetAwaiter方法的类型,该方法没有参数,返回一个awaiter类型的对象。awaiter类型包括以下成员:

           1、bool IsCompleted{get;}

           2、void OnCompleted(Action);

           3、void GetResult();或T GetResult();

           大部分时候,不需要构建自己的awaitable,而应该使用Task类,它是awaitable类型。.Net 4.5 BCL中有很多异步方法,返回Task<T>类型对象。将这些放至await表达式中,它们会在当前线程中异步执行。

           当有需要时如何编写自己的Task<T>类型方法?

           最简单的办法是使用Task.Run创建一个Task,其原型如下:Task Run(Func<TReturn> func)

           要将自己的方法传递给Task.Run方法,要注意的是Task.Run是在不同的线程上运行方法,需要基于该方法创建一个委托。3种不同写法:

        class MyClass
        {
            public int Get10()
            {
                return 10;
            }
            public async Task DoWorkAsync()
            {
                //Func委托,最后一个参数是返回类型,可以有0-16个其他参数
                Func<int> ten = Get10;
                int a = await Task.Run(ten);
                int b = await Task.Run(new Func<int>(Get10));
                int c = await Task.Run(() => { return 10; });
                Console.WriteLine("{0} {1} {2}", a, b, c);
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                Task t = (new MyClass()).DoWorkAsync();
                t.Wait();
                Console.ReadLine();
            }
        }

    上面的例子中,Task.Run的签名是以Func<TResult>为参数。其他的重载还包括:

    Task.Run(Action action)

    Task.Run(Action action,CancellationToken token)

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

    Task<TResult>.Run(Func<TResult> function,CancellationToken token)

    Task.Run(Func<Task> function)

    Task.Run(Func<Task> function,CancellationToken token)

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

    Task<TResult>.Run(Func<Task<TResult>> function,CancellationToken token)

    当遇到特殊的方法不符合时,可以使用接收的Func委托的形式创建一个Lambda函数,这个函数只用于运行特殊方法。例如:Task.Run(()=>GetSum(4,5));

  • 相关阅读:
    pip不是内部或外部命令也不是可运行的程序或批处理文件的问题
    动态规划 leetcode 343,279,91 & 639. Decode Ways,62,63,198
    动态规划 70.climbing Stairs ,120,64
    (双指针+链表) leetcode 19. Remove Nth Node from End of List,61. Rotate List,143. Reorder List,234. Palindrome Linked List
    建立链表的虚拟头结点 203 Remove Linked List Element,82,147,148,237
    链表 206 Reverse Linked List, 92,86, 328, 2, 445
    (数组,哈希表) 219.Contains Duplicate(2),217 Contain Duplicate, 220(3)
    重装系统
    java常用IO
    端口
  • 原文地址:https://www.cnblogs.com/NicolasLiaoran/p/12936369.html
Copyright © 2011-2022 走看看