public static IReadOnlyCollection <Exception> ForEach <T>( IEnumerable <T> items, Action <T> handleItem, DegreeOfParallelism?degreeOfParallelism = default, CancellationToken cancellationToken = default) { var threadCount = (degreeOfParallelism ?? DegreeOfParallelism.Default).Value; var exceptions = new ConcurrentBag <Exception>(); using var blockingItems = new BlockingCollection <T>(new ConcurrentQueue <T>()); var threads = Enumerable .Range(0, threadCount) .Select(index => new Thread(() => { ForEachThreadLoop( blockingItems, handleItem, exceptions, cancellationToken); }) { Name = $"{nameof(ParallelExecution)} [{index}]", }) .ToList(); threads.ForEach(t => t.Start()); blockingItems.AddRangeSafe(items, cancellationToken); blockingItems.CompleteAdding(); threads.ForEach(t => t.Join()); if (cancellationToken.IsCancellationRequested) { exceptions.Add(new OperationCanceledException(cancellationToken)); } return(exceptions); }