异步编程
基本概念
注意
- 异步方法的返回值一般是
Task<T>
,T 是真正的返回值类型,例如:Task<int>
; - 即使方法没有返回值,也最好把返回值声明为 非泛型类的
Task
; - 调用异步方法时,一般在方法前加上
await
关键字,这样拿到的返回值就是泛型指定的 T 类型的结果; - 异步方法具有“传染性”,一个方法中如果使用了
await
调用,则这个方法外层必须使用async
进行修饰;
示例:
C#
static async Task Main(string[] args)
{
string filename = @"E:\projects\NetCore\awaitasync\测试.txt";
await File.WriteAllTextAsync(filename, "HELLO");
string s = await File.ReadAllTextAsync(filename);
Console.WriteLine(s);
}
异步委托
文章阅读:https://blog.csdn.net/HerryDong/article/details/82825548
C#
//重点是 await + async 的使用
ThreadPool.QueueUserWorkItem(async (obj) =>
{
while (true)
{
string filename = @"E:\projects\NetCore\awaitasync\测试.txt";
await File.WriteAllTextAsync(filename, "AAAAAAAAA");
}
});
不使用async+await的情况
async
方法会有缺点:
异步方法会生成一个类,运行效率并没有普通方法高;
可能会占用非常多的线程;
所以,如果一个异步方法只是对别的异步方法调用的转发(就是简单的返回值),并没有太多的逻辑(如等待A方法的结果,利用A的结果再调用B方法等等),那么此时我们就可以不使用 async+await 进行简化。
请看下面的两个例子:
C#
static async Task Main(string[] args)
{
string s = await ReadAasync1(1);
Console.WriteLine(s);
}
// 正常写法
static async Task<string> ReadAasync(int num)
{
if (num == 1)
return await File.ReadAllTextAsync(@"E:\projects\NetCore\awaitasync\测试.txt");
else if (num == 2)
return await File.ReadAllTextAsync(@"E:\projects\NetCore\awaitasync\测试2.txt");
}
重点思考:此时函数的返回值需要为 Task<string>
类型, 而 ReadAllTextAsync() 返回值就是 Task<string>
类型,直接返回就可以了呀,所以不用关键字修饰!
C#
// 省略 async + await 的写法
static Task<string> ReadAasync1(int num)
{
if (num == 1)
return File.ReadAllTextAsync(@"E:\projects\NetCore\awaitasync\测试.txt");
else if (num == 2)
return File.ReadAllTextAsync(@"E:\projects\NetCore\awaitasync\测试2.txt");
}
whenAll()
WhenAny()
:任何一个 Task 完成,Task 就完成;WhenAll()
:所有 Task 方法完成,Task 才会完成,用于等待多个任务执行结束,但是不在乎他们的执行顺序;
C#
Task<string> s1 = File.ReadAllTextAsync(@"E:\projects\NetCore\awaitasync\测试.txt");
Task<string> s2 = File.ReadAllTextAsync(@"E:\projects\NetCore\awaitasync\测试2.txt");
Task<string> s3 = File.ReadAllTextAsync(@"E:\projects\NetCore\awaitasync\测试3.txt");
string[] res = await Task.WhenAll(s1, s2, s3);
异步和yield
参考文章:
yield return
是一个强大的关键字,它可以帮助我们在 不创建临时集合 的情况下,实现枚举的值的生成。yield return
可以实现延迟执行,更具可读性和内存优化的使用。
当我们使用 yield return
时,编译器会为我们生成一个名为 “Enumerator” 的状态机,这个状态机将记录每次迭代的状态,从而从上一次迭代的地方继续执行,而不需要重新开始。这使得我们可以在循环中逐个返回值,而无需一次性返回所有值。
C#
static IEnumerable<string> Test1()
{
List<string> list = new List<string>();
list.Add("hello");
list.Add("sunny");
list.Add("happy");
return list;
}
C#
//不用创建临时集合
static IEnumerable<string> Test2()
{
yield return "hello";
yield return "sunny";
yield return "happy";
}
注意
在旧版C#中,async 方法中不能使用 yield。但是从 C#8.0 以后,可以在异步方法中把返回值声明为 IAsyncEnumerable
(不要带Task),然后遍历时使用 await foreach()
即可。
C#
await foreach (var item in Test3())
{
Console.WriteLine(item);
}
static async IAsyncEnumerable<string> Test3()
{
yield return "hello";
yield return "sunny";
yield return "happy";
}