《C# in depth》第5章C#5.0中的更改(十三)——異步枚舉器

一、異步枚舉
异步枚举器(Async Enumerator)是指一种异步迭代器,可以用于处理异步数据源。它允许我们以异步的方式逐个读取数据源中的元素。

在传统的同步枚举器中,当我们遍历一个集合时,程序会等待每个元素返回后才能继续执行下一个操作。而在异步枚举器中,我们可以在等待当前元素返回时同时执行其他操作。

异步枚举器通常使用 async/await 关键字来实现异步操作。例如,在 C# 中,我们可以使用以下代码创建一个异步枚举器:

public async IAsyncEnumerator<int> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
    for (int i = 0; i < 10; i++)
    {
        await Task.Delay(1000, cancellationToken); // 模拟异步操作
        yield return i;
    }
}

通过上述代码,我们可以创建一个异步枚举器,其中每隔1秒钟返回一个整数。在使用这个异步枚举器时,我们可以使用 await foreach 语法进行遍历:

await foreach (var item in GetAsyncEnumerator())
{
    Console.WriteLine(item);
}

这将输出从0到9的整数,并且在每次等待延迟期间不会阻塞主线程,因为异步枚举器的枚举操作是异步执行的。

二、異步並行枚舉器
并行异步枚举器是一种用于处理大量数据集合的工具,它能够同时处理多个项并在必要时异步返回结果。下面是一个示例代码,用于演示如何创建并行异步枚举器:

public async Task<IEnumerable<T>> ParallelAsyncEnumerator<T>(
    IEnumerable<Task<T>> source, int degreeOfParallelism)
{
    var enumerator = source.GetEnumerator();
    var tasks = new List<Task<T>>(degreeOfParallelism);
    for (int i = 0; i < degreeOfParallelism; i++)
    {
        if (!enumerator.MoveNext())
        {
            break;
        }
        tasks.Add(enumerator.Current);
    }
    while (tasks.Count > 0)
    {
        var completedTask = await Task.WhenAny(tasks);
        tasks.Remove(completedTask);
        if (enumerator.MoveNext())
        {
            tasks.Add(enumerator.Current);
        }
        yield return completedTask.Result;
    }
}

是不是有些惱火,沒關係,兄弟,給你加上註釋

点击查看代码
// 定义了一个异步方法 ParallelAsyncEnumerator,该方法使用泛型参数 T,接受两个参数
// 参数1:一个 IEnumerable<Task<T>> 类型的集合,包含了一组需要并行执行的 Task 对象
// 参数2:一个整数类型的 degreeOfParallelism,表示最大并发数
public async Task<IEnumerable<T>> ParallelAsyncEnumerator<T>(
    IEnumerable<Task<T>> source, int degreeOfParallelism)
{
    // 获取 source 的枚举器对象
    var enumerator = source.GetEnumerator();
    // 创建一个空的 Task 列表 tasks,用于存储正在执行的任务
    var tasks = new List<Task<T>>(degreeOfParallelism);
    // 循环 degreeOfParallelism 次,如果还有未处理的任务,则将其添加到 tasks 中
    for (int i = 0; i < degreeOfParallelism; i++)
    {
        // 如果已经没有未处理的任务,则退出循环
        if (!enumerator.MoveNext())
        {
            break;
        }
        // 将当前枚举器指向的 Task 添加到 tasks 中
        tasks.Add(enumerator.Current);
    }
    // 当存在正在执行的任务时进行遍历,直至所有的任务都执行完毕
    while (tasks.Count > 0)
    {
        // 等待任意一个 Task 执行完成
        var completedTask = await Task.WhenAny(tasks);
        // 将已完成的任务从 tasks 列表中移除
        tasks.Remove(completedTask);
        // 如果仍然存在未处理的任务,则将其添加到 tasks 列表中
        if (enumerator.MoveNext())
        {
            tasks.Add(enumerator.Current);
        }
        // 返回已完成的任务的结果
        yield return completedTask.Result;
    }
}

这里的 ParallelAsyncEnumerator 方法接受两个参数:源集合 source 和并行度 degreeOfParallelism。该方法首先创建一个枚举器,然后使用 degreeOfParallelism 创建若干个任务,并从枚举器中获取前 degreeOfParallelism 个元素赋值给这些任务。接下来,在一个 while 循环中,该方法通过 Task.WhenAny 等待任意一个任务完成,并将已完成的任务从任务列表中移除。如果枚举器还有剩余元素,则获取下一个元素,并将其作为新任务添加到任务列表中。最后,通过 yield return 返回已完成任务的结果。

使用并行异步枚举器,可以同时处理多个任务,从而提高程序的效率。例如,在下载大量文件时,可以使用并行异步枚举器同时下载多个文件,从而加速整个过程。

posted @ 2023-04-26 13:13  GroundSoft  阅读(40)  评论(0编辑  收藏  举报