5.1C#异步编程运行机制

C#语言的异步编程是基于任务的异步编程(Task Asynchronous Programming,简写TAP)模型,该模型的核心是 Task 和 Task<T> 对象以及 async 和 await 关键字。

1.异步方法声明

async 返回类型 CurrentMethodAsync() 
{
    //启动多个任务,并将结果存储在任务变量
    返回类型1 任务变量1 = AnotherMethod1Async();
    返回类型2 任务变量2 = AnotherMethod2Async();
    返回类型N 任务变量N = AnotherMethodNAsync();

    //挂起当前异步方法,当任务变量有结果时,从任务变量查询结果
    //并将结果保存在变量
    类型1 变量1 = await 任务变量1;
    类型2 变量2 = await 任务变量2;
    类型N 变量N = await 任务变量N;
    
    //读取变量内容,返回值
    [return 表达式;]
}

async 修饰符声明 方法、lambda表达式、匿名方法是异步的。

await 运算符等待另一个异步方法返回的任务变量。仅允许在异步方法中且至少使用一个 await 运算符。

await 运算符会立即暂停往下执行同时将当前异步方法挂起,并且控制权返回到当前异步方法的调用方,当 任务变量 有结果时,控制权将在此处恢复继续执行,从 任务变量 查询结果并赋值给变量。

await 运算符应用到表示已完成操作的操作数时,它将立即返回操作的结果,而不会暂停其所属的方法。

通过这两个关键字,可以轻松地创建异步方法(几乎与创建同步方法一样轻松)。

2.返回类型

返回类型类型描述
Task引用类型如果方法内 return 语句不返回操作数 或 不含任何 return 语句,则将 Task 用作返回类型。
Task<TResult>引用类型如果方法内 return 语句的操作数返回类型为 TResult 类型,则将 Task<TResult> 用作返回类型。
void值类型如果要编写需要 void 返回类型的异步事件处理程序(async event handler)。
任何具有可访问的 GetAwaiter 方法的类型引用类型GetAwaiter 方法返回的awaiter类型的实例必须实现 System.Runtime.CompilerServices.ICriticalNotifyCompletion 接口且必须具有 System.Runtime.CompilerServices.AsyncMethodBuilderAttribute 特性。比如ValueTaskValueTask<TResult>
IAsyncEnumerable<T>引用类型 用于返回异步流的异步方法。

3.等待任务

用这个而不是这个应用场景
awaitTask.Wait 或 Task.Result检索后台任务的结果
await Task.WhenAnyTask.WaitAny等待任何任务完成
await Task.WhenAllTask.WaitAll等待所有任务完成
await Task.DelayThread.Sleep等待一段时间

4.运行机制

public async Task<int> GetUrlContentLengthAsync()
{
    var client = new HttpClient();

    Task<string> getStringTask =
        client.GetStringAsync("https://docs.microsoft.com/dotnet");

    DoIndependentWork();

    string contents = await getStringTask;

    return contents.Length;
}

void DoIndependentWork()
{
    Console.WriteLine("Working...");
}
5.1C#异步编程运行机制

运行机制基本分为两步:

4.1启动 Task<string>任务

Task<string> getStringTask =
    client.GetStringAsync("https://docs.microsoft.com/dotnet");

4.2等待 Task<string>任务

string contents = await getStringTask;

5.异步数据流

await foreach语句迭代异步数据流(实现IAsyncEnumerable<T>接口的集合类型)的实例。

static async IAsyncEnumerable<string> ReadWordsFromStreamAsync()
{
    string data =
        @"This is a line of text.
          Here is the second line of text.
          And there is one more for good measure.
          Wait, that was the penultimate line.";

    using var readStream = new StringReader(data);

    string line = await readStream.ReadLineAsync();
    while (line != null)
    {
        foreach (string word in line.Split(' ', StringSplitOptions.RemoveEmptyEntries))
        {
            yield return word;
        }

        line = await readStream.ReadLineAsync();
    }
}
await foreach (var item in ReadWordsFromStreamAsync())
{
    Console.WriteLine(item);
}

await foreach语句还可以迭代满足以下条件的任何类型的实例:

  • 类型具有public无参数的GetAsyncEnumerator方法(可以是类型的扩展方法),GetAsyncEnumerator方法的返回值具有publicCurrent属性。
  • 类型具有public的无参数MoveNextAsync方法,MoveNext方法的返回值为Task<bool>ValueTask<bool>或 任何其它awaitable类型,awaitable类型的awaiter的 GetResult 方法返回 bool 值。

原创文章,作者:huoxiaoqiang,如若转载,请注明出处:https://www.huoxiaoqiang.com/csharp/csharplang/11607.html

(0)
上一篇 2021年5月1日 17:26
下一篇 2021年5月2日 02:32

相关推荐

  • 3.12C#类型之记录结构(record struct)和记录类(record class)

    1.记录声明 声明记录结构(值类型)或记录类(引用类型)的语法与声明结构或类的语法相似,可使用位置参数或属性语法声明记录类型。 记录类型可以是不可变类型,也可以是可变类型,但记录类型的发明主要是为创建自定义不可变类型提供简洁的语法。 编译器会为记录的每个位置参数自动创建public自动实现的属性,如果生成的自动实现的属…

    C#语言教程 2021年3月12日
    05450
  • 3.14C#类型之指针(Pointer)

    1.指针类型声明 类型为referent类型*的指针变量存储的是类型为referent类型的固定变量或可移动变量的地址。只有非托管类型可为referent类型。 任何指针类型可以分配 null。 2.继承 指针类型没有从 System.Object 继承。 3.类型转换 指针类型与 Sys…

    C#语言教程 2021年3月14日
    04920
  • 3.1C#语言的命名空间(Namespace)

    1.命名空间声明 C# 10 中可使用文件范围的命名空间声明,一个文件中的所有类型都在一个命名空间中。文件范围的命名空间不能声明嵌套的命名空间或第二个文件范围的命名空间。 可以在两个或多个声明中定义一个命名空间。 2.using指令 2.1导入其它命名空间的类型 2.2创建命名空间别名 using 指令的声明…

    C#语言教程 2021年3月1日
    06490

发表回复

登录后才能评论