.net 如何正确地使用异步方法编写Parallel.For [重复]

5gfr0r5j  于 2023-04-22  发布在  .NET
关注(0)|答案(4)|浏览(168)

此问题已在此处有答案

Parallel foreach with asynchronous lambda(10个答案)
11天前关闭
我应该如何构造下面的代码,以便调用async方法?

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

w51jfk4q1#

Parallel.For()不能很好地与async方法一起工作。如果你不需要限制并行度(即你可以同时执行所有任务),你可以简单地启动所有Task,然后等待它们完成:

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();
wdebmtf2

wdebmtf22#

你可以试试我用的这个代码。它使用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();
        }
    }
}

使用方法:

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

有什么建议请告诉我。

mbjcgjjk

mbjcgjjk3#

我建议你看看我几天前问过的这个问题,最后我自己回答了,基本上我在寻找一个并行和异步的ForEach方法
该方法使用SemaphoreSlim来并行处理事物,并接受异步方法作为输入操作。
你可能还想看看我在回答的最后提供的两个链接,它们对实现这种行为非常有帮助,它们还包含另一种使用Partitioner的方法。
就我个人而言,我不喜欢Parallel.For,因为它是一个同步调用,正如我给出的链接中所解释的那样;我希望它都是“异步”的:-)
这就是:Asynchronously and parallelly downloading files

gwbalxhn

gwbalxhn4#

Parallel.For中调用async方法的最简单方法是:

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

==============
MarioDS在评论中提到的绝对正确,在这种情况下,你可能会有未观察到的异常。这绝对是非常重要的事情,你应该永远记住,然后处理异步委托。
在这种情况下,如果你认为你会有异常,你可以在delegate中使用try/catch块。或者在某些情况下,如果你的情况适合,你可以订阅TaskScheduler.UnobservedTaskException事件。

相关问题