限制并行执行的异步方法的数量

时间:2018-02-15 13:22:34

标签: c# multithreading asynchronous async-await task-parallel-library

我有一个关于异步方法的并行执行的问题。

我想限制将同时执行的异步方法的数量(以限制实际上在异步方法中发送的另一个系统的Web请求数)。

那么最好的方法是什么? 我通过使用Parallel找到了解决方案,并设置了DegreeOfParallelism,但我不太喜欢这个解决方案,因为它会阻止线程数等于DOP(并行度)。

所以,这是使用Parallel的代码:

var results = dataProvider.GetResults(someIdParam);
            var average = results.AsParallel().WithDegreeOfParallelism(5).Average(x =>
            {
                var t = GetSSIM(x);
                t.Wait();
                return t.Result;
            });

因此,这将起作用并限制同时发出的请求数,但会阻塞5个线程。

我最终编写了自己的方法:

    public static async Task<IEnumerable<T2>> ProcessEachAsync<T1, T2>(IEnumerable<T1> src, Func<T1, Task<T2>> func, int dop)
    {
        var workers = new Task<T2>[Math.Min(dop, src.Count())]; //to make sure that we will not have nulls in workers collection
        var result = new List<T2>();

        int counter = 0;
        int index = 0;
        foreach(var element in src)
        {
            if (counter < dop)
                index = counter++;
            else
            {
                var ended = await Task.WhenAny(workers);
                index = Array.FindIndex(workers, x => x == ended);
                result.Add(ended.Result);
            }

            var t = func(element);
            t.Start();
            workers[index] = t;
        }

        Task.WaitAll(workers);
        result.AddRange(workers.Select(x => x.Result));
        return result;
    }

小心!!!!!此代码尚未经过测试且存在错误! 但它解释了主要想法

因此,此解决方案将仅阻止1个线程。也许有更简单的方法来达到我想要的目的?

1 个答案:

答案 0 :(得分:0)

感谢@evk和我的同事帮我解决了这个问题。 所以我用SemaphoreSlim实现了一个解决方案。它的缺点是它将所有数据转换为任务,但代码非常漂亮,我将留下它:)

    public static async Task<IEnumerable<T2>> ProcessEachAsync<T1, T2>(IEnumerable<T1> src, Func<T1, Task<T2>> func, int dop)
    {
        using (var semSlim = new SemaphoreSlim(dop))
        {
            var result = new ConcurrentBag<T2>();
            Func<T1, Task> getTask = async (x) =>
            {
                try
                {
                    await semSlim.WaitAsync();
                    var res = await func(x);
                    result.Add(res);
                }
                finally
                {
                    semSlim.Release();
                }
            };

            await Task.WhenAll(src.Select(x => getTask(x)));
            return result;
        }
    }

<强>小心!没有测试!!!!

谢谢大家!