Example #1
0
 public static async Task ForEachAsync <T>(IEnumerable <T> container, Action <T, CancellationToken> action, CancellationToken token, int threadCount = -1)
 {
     ThrowIfInvalidForEachArgument(container, action);
     await ParallelForEachContext <T> .ForEachAsync(container, action, token, threadCount);
 }
        /// <summary>
        /// Invokes an asynchronous action on each item in the collection in parallel
        /// </summary>
        /// <typeparam name="T">The type of an item</typeparam>
        /// <param name="enumerator">The collection of items to perform actions on</param>
        /// <param name="asyncItemAction">An asynchronous action to perform on the item, where first argument is the item and second argument is item's index in the collection</param>
        /// <param name="maxDegreeOfParallelism">Maximum items to schedule processing in parallel. The actual concurrency level depends on TPL settings. Set to 0 to choose a default value based on processor count.</param>
        /// <param name="breakLoopOnException">Set to True to stop processing items when first exception occurs. The result <see cref="AggregateException"/> might contain several exceptions though when faulty tasks finish at the same time.</param>
        /// <param name="gracefulBreak">If True (the default behavior), waits on completion for all started tasks when the loop breaks due to cancellation or an exception</param>
        /// <param name="cancellationToken">Cancellation token</param>
        /// <exception cref="ParallelForEachException">Wraps any exception(s) that occurred inside <paramref name="asyncItemAction"/></exception>
        /// <exception cref="OperationCanceledException">Thrown when the loop is canceled with <paramref name="cancellationToken"/></exception>
        public static Task ParallelForEachAsync <T>(
            this IAsyncEnumerator <T> enumerator,
            Func <T, long, Task> asyncItemAction,
            int maxDegreeOfParallelism,
            bool breakLoopOnException,
            bool gracefulBreak,
            CancellationToken cancellationToken = default)
        {
            if (enumerator == null)
            {
                throw new ArgumentNullException(nameof(enumerator));
            }
            if (asyncItemAction == null)
            {
                throw new ArgumentNullException(nameof(asyncItemAction));
            }

            var context = new ParallelForEachContext(maxDegreeOfParallelism, breakLoopOnException, gracefulBreak, cancellationToken);

            Task.Run(
                async() =>
            {
                try
                {
                    var itemIndex = 0L;

                    while (await enumerator.MoveNextAsync().ConfigureAwait(false))
                    {
                        if (context.IsLoopBreakRequested)
                        {
                            break;
                        }

                        await context.OnStartOperationAsync(cancellationToken).ConfigureAwait(false);

                        if (context.IsLoopBreakRequested)
                        {
                            context.OnOperationComplete();
                            break;
                        }

                        Task itemActionTask = null;
                        try
                        {
                            itemActionTask = asyncItemAction(enumerator.Current, itemIndex);
                        }
                        // there is no guarantee that task is executed asynchronously, so it can throw right away
                        catch (Exception ex)
                        {
                            ex.Data["ForEach.Index"] = itemIndex;
                            context.OnOperationComplete(ex);
                        }

                        if (itemActionTask != null)
                        {
                            var capturedItemIndex = itemIndex;
#pragma warning disable CS4014  // Justification: not awaited by design
                            itemActionTask.ContinueWith(
                                task =>
                            {
                                Exception ex = null;
                                if (task.IsFaulted)
                                {
                                    ex = task.Exception;

                                    if (ex is AggregateException aggEx && aggEx.InnerExceptions.Count == 1)
                                    {
                                        ex = aggEx.InnerException;
                                    }

                                    ex.Data["ForEach.Index"] = capturedItemIndex;
                                }

                                context.OnOperationComplete(ex);
                            });
#pragma warning restore CS4014
                        }

                        itemIndex++;
                    }
                }
                catch (Exception ex)
                {
                    context.AddException(ex);
                }
                finally
                {
                    await enumerator.DisposeAsync().ConfigureAwait(false);
                    context.OnOperationComplete();
                }
            });

            return(context.CompletionTask);
        }
Example #3
0
 public static async Task ForEachAsync <T>(IEnumerable <T> container, Action <T> action)
 {
     ThrowIfInvalidForEachArgument(container, action);
     await ParallelForEachContext <T> .ForEachAsync(container, action, -1);
 }