/// <summary> /// Asynchronously runs <paramref name="delegateFunction"/> for every item in the given collection /// </summary> /// <typeparam name="T">Type of the collection item</typeparam> /// <param name="delegateFunction">The function to asynchronously run</param> /// <param name="collection">The collection to be enumerated</param> /// <exception cref="InvalidOperationException">Thrown if execution starts before the end of another operation</exception> public async Task RunForEach <T>(IEnumerable <T> collection, ForEachItemAsync <T> delegateFunction) { if (RunningTasks.Count > 0) { throw new InvalidOperationException(Properties.Resources.AsyncFor_ErrorNoConcurrentExecution); } var exceptions = new List <Exception>(); var taskItemQueue = new Queue <T>(); foreach (var item in collection) { taskItemQueue.Enqueue(item); } TotalTasks = taskItemQueue.Count; // While there's either more tasks to start or while there's still tasks running while (taskItemQueue.Count > 0 || (taskItemQueue.Count == 0 && RunningTasks.Count > 0)) { if ((BatchSize < 1 || RunningTasks.Count < BatchSize) && taskItemQueue.Count > 0) { // We can run more tasks // Get the next task item to run var item = taskItemQueue.Dequeue(); // The item in Collection to process // Start the task var tTask = Task.Run(async() => { await delegateFunction(item).ConfigureAwait(false); IncrementCompletedTasks(); }); // Either wait for it or move on if (RunSynchronously) { await tTask.ConfigureAwait(false); } else { RunningTasks.Add(tTask); } } else { if (RunningTasks.Count > 0) { // We can't start any more tasks, so we have to wait on one. await Task.WhenAny(RunningTasks).ConfigureAwait(false); // Remove completed tasks for (var count = RunningTasks.Count - 1; count >= 0; count--) { if (RunningTasks[count].Exception != null) { var ex = RunningTasks[count].Exception; if (ex is AggregateException aggregateEx && aggregateEx.InnerExceptions.Count == 1) { exceptions.Add(aggregateEx.InnerExceptions.First()); } else { exceptions.Add(RunningTasks[count].Exception); } RunningTasks.RemoveAt(count); } else if (RunningTasks[count].IsCompleted) { RunningTasks.RemoveAt(count); } } }