Skip to content

异步编程

基本概念

注意

  • 异步方法的返回值一般是 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

参考文章:

  1. https://www.cnblogs.com/ljx111/p/17411347.html

  2. https://blog.csdn.net/wojiuguowei/article/details/124604083

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";
}

Released under the MIT License.