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 特性。比如ValueTask 、ValueTask<TResult> 。 |
IAsyncEnumerable<T> | 引用类型 | 用于返回异步流的异步方法。 |
3.等待任务
用这个 | 而不是这个 | 应用场景 |
---|---|---|
await | Task.Wait 或 Task.Result | 检索后台任务的结果 |
await Task.WhenAny | Task.WaitAny | 等待任何任务完成 |
await Task.WhenAll | Task.WaitAll | 等待所有任务完成 |
await Task.Delay | Thread.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...");
}

运行机制基本分为两步:
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
方法的返回值具有public
的Current
属性。 - 类型具有
public
的无参数MoveNextAsync
方法,MoveNext
方法的返回值为
或 任何其它Task<bool>
,ValueTask<bool>
awaitable
类型,awaitable
类型的awaiter
的GetResult
方法返回bool
值。
原创文章,作者:huoxiaoqiang,如若转载,请注明出处:https://www.huoxiaoqiang.com/csharp/csharplang/11607.html