C# s、 是我的。我一次又一次地看到这个问题,所以决定用最简单的方法来解决它,同时也让其他人摆脱困境:)谢谢。。这绝对有道理,帮了我大忙!您有一个输入错误:maxDegreeOfParallelismmaxDegreeOfParallelism正确的拼写确实是

C# s、 是我的。我一次又一次地看到这个问题,所以决定用最简单的方法来解决它,同时也让其他人摆脱困境:)谢谢。。这绝对有道理,帮了我大忙!您有一个输入错误:maxDegreeOfParallelismmaxDegreeOfParallelism正确的拼写确实是,c#,async-await,task-parallel-library,parallel.foreach,C#,Async Await,Task Parallel Library,Parallel.foreach,s、 是我的。我一次又一次地看到这个问题,所以决定用最简单的方法来解决它,同时也让其他人摆脱困境:)谢谢。。这绝对有道理,帮了我大忙!您有一个输入错误:maxDegreeOfParallelismmaxDegreeOfParallelism正确的拼写确实是maxDegreeOfParallelism,但是@shirandor的注释中有一些东西-在您的包中,您错误地调用了变量maxDegreeofparallism(因此,您引用的代码在更改它之前不会编译)我假设您打算从第二个代码块中的await



s、 是我的。我一次又一次地看到这个问题,所以决定用最简单的方法来解决它,同时也让其他人摆脱困境:)谢谢。。这绝对有道理,帮了我大忙!您有一个输入错误:
maxDegreeOfParallelism
maxDegreeOfParallelism
正确的拼写确实是maxDegreeOfParallelism,但是@shirandor的注释中有一些东西-在您的包中,您错误地调用了变量maxDegreeofparallism(因此,您引用的代码在更改它之前不会编译)我假设您打算从第二个代码块中的
await GetData(item)
中删除
await
,因为它会产生编译错误。作为旁注,
ConcurrentBag
可能是一个集合。在这种情况下,
ConcurrentQueue
会更好地为您服务。“使用”不会有帮助。foreach循环将无限期地等待semaphone。只需尝试这个简单的代码就可以重现这个问题:wait Enumerable.Range(1,4).ForEachAsyncConcurrent(async(i)=>{Console.WriteLine(i);throw new Exception(“test Exception”);},maxDegreeOfParallelism:2)@nicolay,anykienko你说得对。内存问题可以通过添加taskswithththrottler.RemoveAll(x=>x.IsCompleted)来解决;我已经在代码中尝试过了,如果maxDegreeOfParallelism不为null,代码就会死锁。在这里,您可以看到所有要复制的代码:当我考虑实现它以供使用时,我对这种方法的担忧是,我正在处理的170万行将导致每个行在tasksWithThrottler列表中都有一个作业,而这似乎并不理想,也不具有真正的可扩展性。发布我的队友和我提出的使用ActionBlock作为单独解决方案的解决方案。@CalebHolt出于兴趣,您最终做了什么……SemaphoreSlim应该用
using
语句包装,因为它实现了IDisposableAlso,这一行“wait throttler.WaitAsync();”不应位于try块内,因为如果它引发异常,则在您尚未获取锁时将调用Release。
tcs.SetResult(null)
需要替换为
tcs.TrySetResult(null)
@Hocas,为什么您认为需要TrySetResult?上次使用此代码时,我在多次调用SetResult时遇到问题)@Hocas,这很有趣。tcs.SetResult(null)预计不会执行两次。使用
信号量lim
CurrentCount
属性控制执行流不是一个好主意。在大多数情况下,它创造了比赛条件。使用Volatile.Read也不可靠(另一种可能的竞争条件)。在生产环境中,我不相信此解决方案。代码中的
任务
是一个
IEnumerable
。它是一个延迟的可枚举集合,而不是具体化的集合。枚举它两次,因此异步方法运行两次。为了防止这种情况发生,您需要事先使用
.ToArray()
.ToList()
将其具体化。谢谢,修复了您可能应该将
信号量.Wait()
替换为
等待信号量.WaitAsync()
,以避免阻塞调用者。还要注意,解决方案中的
信号量lim
功能可以由
操作块的
边界容量
配置以及可等待的
发送异步
方法取代。相比之下,它更有效(记忆方面)。@TheodorZoulias非常感谢您的反馈!这是我正在为一个项目积极努力的事情,所以我将关注这些变化并更新我的解决方案。@TheodorZoulias的回答显示了非常相似的方法。。。大概不会等待操作完成(我在文档中不清楚)Caleb Holt另一个您可能需要注意的问题是,枚举用户提供的
enumerable
可能会导致异常,在这种情况下,您的实现将立即传播此异常,无需等待
操作块的完成
。这不是一个最佳行为,因为它可能会使任务在后台运行而不被观察(以“火与忘”的方式)。正确实现
ForEachAsync
方法可能相当棘手。最近我自己也意识到了这个问题。@AlexeiLevenkov
SendAsync
方法的文档非常混乱。我怀疑这个星球上是否存在过一个足够聪明的人,他仅仅通过阅读文档就能理解这种方法的作用。我们应该深入研究源代码,了解
Post
SendAsync
方法都基于隐藏(显式实现)API,该API有5个可能的返回值。
SendAsync
异步处理
delayed
返回值。“您也可以使用async lambda”很抱歉,我不得不否决您的答案,但将异步委托传递给
并行。ForEach
方法不仅仅是“不是最佳做法”。它有着深刻而无法弥补的缺陷。
Parallel.ForEach
不理解异步委托,因此lambda是
async void
。这不是火灾和遗忘,这是火灾和崩溃。在这种情况下,
Parallel.ForEach
不会等待启动的操作完成,它不会强制执行最大程度的并行,也不会传播异常。任何异常都将无法处理,并将使流程崩溃。好吧,你不能通过展示坏例子和间接推广坏做法来期待好的投票,不管你是否推荐它们。把你的答案中所有不好的部分去掉,保留好的部分怎么样?这句话“你也可以使用异步lambda”,与
Parallel.ForEach
方法联系在一起,是我无可争议的反对票。不多
var bag = new ConcurrentBag<object>();
Parallel.ForEach(myCollection, async item =>
{
  // some pre stuff
  var response = await GetData(item);
  bag.Add(response);
  // some post stuff
}
var count = bag.Count;
var bag = new ConcurrentBag<object>();
Parallel.ForEach(myCollection, item =>
{
  // some pre stuff
  var responseTask = await GetData(item);
  responseTask.Wait();
  var response = responseTask.Result;
  bag.Add(response);
  // some post stuff
}
var count = bag.Count;
var bag = new ConcurrentBag<object>();
var tasks = myCollection.Select(async item =>
{
  // some pre stuff
  var response = await GetData(item);
  bag.Add(response);
  // some post stuff
});
await Task.WhenAll(tasks);
var count = bag.Count;
using Dasync.Collections;

var bag = new ConcurrentBag<object>();
await myCollection.ParallelForEachAsync(async item =>
{
  // some pre stuff
  var response = await GetData(item);
  bag.Add(response);
  // some post stuff
}, maxDegreeOfParallelism: 10);
var count = bag.Count;
    /// <summary>
    /// Concurrently Executes async actions for each item of <see cref="IEnumerable<typeparamref name="T"/>
    /// </summary>
    /// <typeparam name="T">Type of IEnumerable</typeparam>
    /// <param name="enumerable">instance of <see cref="IEnumerable<typeparamref name="T"/>"/></param>
    /// <param name="action">an async <see cref="Action" /> to execute</param>
    /// <param name="maxDegreeOfParallelism">Optional, An integer that represents the maximum degree of parallelism,
    /// Must be grater than 0</param>
    /// <returns>A Task representing an async operation</returns>
    /// <exception cref="ArgumentOutOfRangeException">If the maxActionsToRunInParallel is less than 1</exception>
    public static async Task ForEachAsyncConcurrent<T>(
        this IEnumerable<T> enumerable,
        Func<T, Task> action,
        int? maxDegreeOfParallelism = null)
    {
        if (maxDegreeOfParallelism.HasValue)
        {
            using (var semaphoreSlim = new SemaphoreSlim(
                maxDegreeOfParallelism.Value, maxDegreeOfParallelism.Value))
            {
                var tasksWithThrottler = new List<Task>();

                foreach (var item in enumerable)
                {
                    // Increment the number of currently running tasks and wait if they are more than limit.
                    await semaphoreSlim.WaitAsync();

                    tasksWithThrottler.Add(Task.Run(async () =>
                    {
                        await action(item).ContinueWith(res =>
                        {
                            // action is completed, so decrement the number of currently running tasks
                            semaphoreSlim.Release();
                        });
                    }));
                }

                // Wait for all tasks to complete.
                await Task.WhenAll(tasksWithThrottler.ToArray());
            }
        }
        else
        {
            await Task.WhenAll(enumerable.Select(item => action(item)));
        }
    }
await enumerable.ForEachAsyncConcurrent(
    async item =>
    {
        await SomeAsyncMethod(item);
    },
    5);
public static class AsyncEx
{
    public static async Task ParallelForEachAsync<T>(this IEnumerable<T> source, Func<T, Task> asyncAction, int maxDegreeOfParallelism = 10)
    {
        var semaphoreSlim = new SemaphoreSlim(maxDegreeOfParallelism);
        var tcs = new TaskCompletionSource<object>();
        var exceptions = new ConcurrentBag<Exception>();
        bool addingCompleted = false;

        foreach (T item in source)
        {
            await semaphoreSlim.WaitAsync();
            asyncAction(item).ContinueWith(t =>
            {
                semaphoreSlim.Release();

                if (t.Exception != null)
                {
                    exceptions.Add(t.Exception);
                }

                if (Volatile.Read(ref addingCompleted) && semaphoreSlim.CurrentCount == maxDegreeOfParallelism)
                {
                    tcs.TrySetResult(null);
                }
            });
        }

        Volatile.Write(ref addingCompleted, true);
        await tcs.Task;
        if (exceptions.Count > 0)
        {
            throw new AggregateException(exceptions);
        }
    }
}
await Enumerable.Range(1, 10000).ParallelForEachAsync(async (i) =>
{
    var data = await GetData(i);
}, maxDegreeOfParallelism: 100);
var bag = new ConcurrentBag<object>();
var maxParallel = 20;
var throttler = new SemaphoreSlim(initialCount: maxParallel);
var tasks = myCollection.Select(async item =>
{
  try
  {
     await throttler.WaitAsync();
     var response = await GetData(item);
     bag.Add(response);
  }
  finally
  {
     throttler.Release();
  }
});
await Task.WhenAll(tasks);
var count = bag.Count;
var tasks = myCollection.Select(GetData).ToList();
await Task.WhenAll(tasks);
var results = tasks.Select(t => t.Result);
    public static async Task ForEachAsyncConcurrent<T>(this IAsyncEnumerable<T> enumerable, Func<T, Task> action, int maxDegreeOfParallelism, int? boundedCapacity = null)
    {
        ActionBlock<T> block = new ActionBlock<T>(
           action, 
           new ExecutionDataflowBlockOptions 
           { 
             MaxDegreeOfParallelism = maxDegreeOfParallelism, 
             BoundedCapacity = boundedCapacity ?? maxDegreeOfParallelism * 3 
           });

        await foreach (T item in enumerable)
        {
           await block.SendAsync(item).ConfigureAwait(false);
        }

        block.Complete();
        await block.Completion;
    }
var options = new ParallelOptions { MaxDegreeOfParallelism = 5 }
Task.Run(() =>
{
    Parallel.ForEach(myCollection, options, item =>
    {
        DoWork(item);
    }
}
public static async Task ParallelForEachAsync<T>(this IEnumerable<T> source, Func<T, Task> asyncAction, int maxDegreeOfParallelism)
{
    var throttler = new SemaphoreSlim(initialCount: maxDegreeOfParallelism);
    var tasks = source.Select(async item =>
    {
        await throttler.WaitAsync();
        try
        {
            await asyncAction(item).ConfigureAwait(false);
        }
        finally
        {
            throttler.Release();
        }
    });
    await Task.WhenAll(tasks);
}