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

C# 如何使用异步方法正确编写Parallel.For,c#,.net,task-parallel-library,async-await,C#,.net,Task Parallel Library,Async Await,如何构造下面的代码,以便调用异步方法 Parallel.For(0, elevations.Count(), delegate(int i) { allSheets.AddRange(await BuildSheetsAsync(userID, elevations[i], includeLabels)); }); 我建议你看看我几天前问的这个问题,最后我回答了自己,基本上我在寻找一种并行和异步的ForEach方法 该方法使用信号量lim并行处理事物,并接受异步方法作为输入操作 您可能还

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

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

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

该方法使用
信号量lim
并行处理事物,并接受异步方法作为输入操作

您可能还想看看我在回答末尾提供的两个链接,它们对实现这种行为非常有帮助,并且它们还包含使用
分区器来实现这一点的另一种方法

就我个人而言,我不喜欢并行的
;我想要所有的“异步”:)

这里是:

Parallel.For()
async
方法不兼容。如果您不需要限制并行度(即,您可以同时执行所有任务),只需启动所有
任务
s,然后等待它们完成:

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();
var tasks=Enumerable.Range(0,elevations.Count())
.Select(i=>BuildSheetsAsync(userID,elevations[i],includeLabels));
列出所有工作表=(等待任务.WhenAll(任务))。选择many(x=>x).ToList();

Parallel中调用异步方法的最简单方法。对于
,下面是:

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

评论中提到的绝对正确,在这种情况下,您可能有未观察到的例外情况。这绝对是非常重要的事情,您应该始终牢记这一点,然后处理异步委托


在这种情况下,如果您认为会出现异常,可以在委托内使用
try/catch
block。或者在某些情况下,如果您的情况适合,您可以在活动上订阅。

您可以尝试我正在使用的代码。它使用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();
        }
    }
}

任何建议请告诉我。

+1,除此之外,
信号量lim
显然在OP的情况下是不需要的。我还建议您在代码中使用带超时的
CancellationTokenSource
而不是
Timer
对象。我无法添加它要求的IEnumerable列表集合。我怎样才能让它工作?var task=Enumerable.Range(0,elevations.Length)。选择(i=>BuildSheetsAsync(userID,elevations[i],includeLabels));List allSheets=新列表();allSheets.AddRange(wait Task.WhenAll(Task));当I allSheets.AddRange(wait Task.WhenAll(Task))@啊,我没有注意到
BuildSheetsAsync()
已经返回了一个集合。如果要从集合集合中生成单个集合,可以使用
SelectMany()
。要从
IEnumerable
集合中列出一个列表,请使用
ToList()
“Parallel.For()与异步方法不兼容。”-为什么它们不能很好地协同工作?它们是帮助提高CPU效率的两个不同概念。“如果您不需要限制并行度…”-做出这种假设似乎适得其反。一个好的程序会根据其运行的服务器的功能故意限制并行度,这可能会发生变化。@Sean它们不能很好地协同工作,因为对于
wait
,处理在返回的
任务完成时结束,而不是在函数返回时结束。但是,
Parallel.For
不理解这一点,并假设函数在返回时结束。限制并行度可能很重要,但也可能很复杂。如果我不知道复杂的方法是否有必要,我建议从简单的方法开始。你会如何设置一个最大并行度的限制,就像你对并行的限制一样。例如?这会导致错误。异步委托返回
任务
,并行构造不等待该任务。这意味着异常是未被观察到的,在<代码>并行之后您不能确定。对于< /代码>,调用已经运行,所有的工作实际上已经完成(也不能很容易地验证)。可能的副本似乎不考虑当代码>动作(项)< /代码>引发异常时的情况。
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");