欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

利用LINQ实现简单的异步并发

程序员文章站 2022-07-12 19:29:14
...

在.NET应用程序的开发过程中,处于性能考虑,我们可能需要多个互不相关的异步作业同时执行。此时我们可能会想到利用Parallel.ForEach函数,但倘若这些作业是有返回值的,而且我们需要将这些返回值收集起来,那么我们就会需要利用await/async操作,然后目前基本的Parallel.ForEach函数并不支持await/async操作。因此,我们需要另辟蹊径,比如Task.WhenAll。本文的示例代码的实现思路分为两步,一是将完整的作业集分割成若干个子集,之后利用LINQ的Select函数未子集中的每个作业创建一个task thread并运行。

1. 切割数组

参考:https://*.com/questions/419019/split-list-into-sublists-with-linq

public static class ListExtensions
{
    public static IEnumerable<IEnumerable<T>> Split<T>(this List<T> source, int chunkSize)
    {
        var pos = 0;
        while (source.Skip(pos).Any())
        {
            yield return source.Skip(pos).Take(chunkSize);
            pos += chunkSize;
        }
    }
}

2. 并发运行作业

// Simulate the job
public class MyTask
{
    private readonly string _name;
    private static readonly Random Rnd = new Random();
    private static readonly ILog Log = LogManager.GetLogger(typeof(MyTask));

    public MyTask(string name) => _name = name;

    public async Task<int> LongRunAsync()
    {
        var time = Rnd.Next(1, 10);
        Log.Info($"{_name} will sleep for {time} secs.");
        await Task.Delay(time * 1000);
        Log.Info($"{_name} is awake.");
        return time;
    }
}

// Main
class Program
{
    static async Task Main(string[] args)
    {
        var myTasks = new List<MyTask>();
        for (var i = 0; i < 321; ++ i)
        {
            myTasks.Add(new MyTask($"Task_{i}"));
        }

        var timeBag = new List<int>();
        var chunkSize = 50;
        foreach (var tasks in myTasks.Split(chunkSize))
        {
            // Key logic for concurrency
            var taskList = tasks.Select(async x => timeBag.Add(await x.LongRunAsync()));
            await Task.WhenAll(taskList);
            // End
        }
    }
}

值得注意的是,Select函数会为作业集中的每个作业创建一个thread,因此一定要根据实际情况来调整chunkSize的值,以防系统资源被耗尽。

相关标签: 编程及调试