C# 如何使用异步方法正确编写 Parallel.For

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19284202/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 14:37:05  来源:igfitidea点击:

How to correctly write Parallel.For with async methods

c#.nettask-parallel-libraryasync-await

提问by Filling The Stack is What I DO

How would I structure the code below so that the async method gets invoked?

我将如何构造下面的代码以便调用异步方法?

Parallel.For(0, elevations.Count(), delegate(int i)
{
   allSheets.AddRange(await BuildSheetsAsync(userID, elevations[i], includeLabels));
});

采纳答案by svick

Parallel.For()doesn't work well with asyncmethods. If you don't need to limit the degree of parallelism (i.e. you're okay with all of the tasks executing at the same time), you can simply start all the Tasks and then wait for them to complete:

Parallel.For()不适用于async方法。如果您不需要限制并行度(即您可以同时执行所有任务),您可以简单地启动所有Tasks,然后等待它们完成:

var tasks = Enumerable.Range(0, elevations.Count())
    .Select(i => BuildSheetsAsync(userID, elevations[i], includeLabels));
List<Bitmap> allSheets = (await Task.WhenAll(tasks)).SelectMany(x => x).ToList();

回答by Aybe

I'd recommend you to take a look at this question I asked a few days ago and ended-up answering myself, basically I was looking for a parallel and asynchronous ForEach method.

我建议你看一下我几天前问的这个问题,最终我回答了自己,基本上我正在寻找一个并行和异步的 ForEach 方法

The method uses SemaphoreSlimto process things in parallel and it accepts asynchronous methods as an input action.

该方法用于SemaphoreSlim并行处理事物,它接受异步方法作为输入操作。

You might also want to take a look at the two links I have provided at the end of my answer, they have been really helpful for realizing such behavior and they also contain another way of doing this using a Partitionerinstead.

您可能还想看看我在答案末尾提供的两个链接,它们对实现这种行为非常有帮助,并且它们还包含另一种使用 aPartitioner替代的方法。

Personally, I didn't like the Parallel.Forbecause it's a synchronous call as explained in the links I've given; I wanted it all 'async' :-)

就我个人而言,我不喜欢 ,Parallel.For因为它是我提供的链接中解释的同步调用;我想要这一切“异步”:-)

Here it is : Asynchronously and parallelly downloading files

这是:异步和并行下载文件

回答by Yury Kerbitskov

The easiest way to invoke your async method inside Parallel.Foris next:

在内部调用异步方法的最简单方法Parallel.For是下一步:

Parallel.For(0, elevations.Count(), async i =>
{
   allSheets.AddRange(await BuildSheetsAsync(userID, elevations[i], includeLabels));
});

==============

==============

MarioDSmentioned absolutely right in the comment that in that case you may have unobserved exceptions. And this is definitely very important thing which you should always take in mind then have a deal with async delegates.

MarioDS在评论中完全正确地提到,在这种情况下,您可能会有未观察到的异常。这绝对是非常重要的事情,您应该始终牢记这一点,然后与异步委托打交道。

In this case if you think that you will have exceptions you can use try/catchblock inside delegate. Or in some cases if your situation is good for it you can subscribe on TaskScheduler.UnobservedTaskExceptionevent.

在这种情况下,如果您认为会有异常,您可以try/catch在委托中使用块。或者在某些情况下,如果您的情况适合,您可以订阅TaskScheduler.UnobservedTaskException事件。

回答by Alex Zhang

You can try this code I'm using. it using foreach and SemaphoreSlim to achive parallel asynchronous.

你可以试试我正在使用的这段代码。它使用 foreach 和 SemaphoreSlim 来实现并行异步。

public static class ParallelAsync
{
    public static async Task ForeachAsync<T>(IEnumerable<T> source, int maxParallelCount, Func<T, Task> action)
    {
        using (SemaphoreSlim completeSemphoreSlim = new SemaphoreSlim(1))
        using (SemaphoreSlim taskCountLimitsemaphoreSlim = new SemaphoreSlim(maxParallelCount))
        {
            await completeSemphoreSlim.WaitAsync();
            int runningtaskCount = source.Count();

            foreach (var item in source)
            {
                await taskCountLimitsemaphoreSlim.WaitAsync();

                Task.Run(async () =>
                {
                    try
                    {
                        await action(item).ContinueWith(task =>
                        {
                            Interlocked.Decrement(ref runningtaskCount);
                            if (runningtaskCount == 0)
                            {
                                completeSemphoreSlim.Release();
                            }
                        });
                    }
                    finally
                    {
                        taskCountLimitsemaphoreSlim.Release();
                    }
                }).GetHashCode();
            }

            await completeSemphoreSlim.WaitAsync();
        }
    }
}

usage:

用法:

string[] a = new string[] {
    "1",
    "2",
    "3",
    "4",
    "5",
    "6",
    "7",
    "8",
    "9",
    "10",
    "11",
    "12",
    "13",
    "14",
    "15",
    "16",
    "17",
    "18",
    "19",
    "20"
};

Random random = new Random();

await ParallelAsync.ForeachAsync(a, 2, async item =>
{
    Console.WriteLine(item + " start");

    await Task.Delay(random.Next(1500, 3000));
    Console.WriteLine(item + " end");
});

Console.WriteLine("All finished");

any suggestion please let me know.

任何建议请让我知道。