static void HandleExceptions(IEnumerable <Task> tasks, ParallelLoopState.ExternalInfos infos) { List <Exception> exs = new List <Exception> (); foreach (Task t in tasks) { if (t.Exception != null) { exs.Add(t.Exception); } } if (exs.Count > 0) { if (infos != null) { infos.IsExceptional = true; } throw new AggregateException(exs).Flatten(); } }
public static ParallelLoopResult For <TLocal> (int fromInclusive, int toExclusive, ParallelOptions parallelOptions, Func <TLocal> localInit, Func <int, ParallelLoopState, TLocal, TLocal> body, Action <TLocal> localFinally) { if (body == null) { throw new ArgumentNullException("body"); } if (localInit == null) { throw new ArgumentNullException("localInit"); } if (localFinally == null) { throw new ArgumentNullException("localFinally"); } if (parallelOptions == null) { throw new ArgumentNullException("options"); } if (fromInclusive >= toExclusive) { return(new ParallelLoopResult(null, true)); } // Number of task toExclusive be launched (normally == Env.ProcessorCount) int step; int num = GetBestWorkerNumber(fromInclusive, toExclusive, parallelOptions, out step); Task[] tasks = new Task [num]; StealRange[] ranges = new StealRange[num]; for (int i = 0; i < num; i++) { ranges[i] = new StealRange(fromInclusive, i, step); } ParallelLoopState.ExternalInfos infos = new ParallelLoopState.ExternalInfos(); int currentIndex = -1; Action workerMethod = delegate { int localWorker = Interlocked.Increment(ref currentIndex); StealRange range = ranges[localWorker]; int index = range.V64.Actual; int stopIndex = localWorker + 1 == num ? toExclusive : Math.Min(toExclusive, index + step); TLocal local = localInit(); ParallelLoopState state = new ParallelLoopState(infos); CancellationToken token = parallelOptions.CancellationToken; try { for (int i = index; i < stopIndex;) { if (infos.IsStopped) { return; } token.ThrowIfCancellationRequested(); if (i >= stopIndex - range.V64.Stolen) { break; } if (infos.LowestBreakIteration != null && infos.LowestBreakIteration > i) { return; } state.CurrentIteration = i; local = body(i, state, local); if (i + 1 >= stopIndex - range.V64.Stolen) { break; } range.V64.Actual = ++i; } bool sixtyfour = IntPtr.Size == 8; // Environment.Is64BitProcess; // Try toExclusive steal fromInclusive our right neighbor (cyclic) int len = num + localWorker; for (int sIndex = localWorker + 1; sIndex < len; ++sIndex) { int extWorker = sIndex % num; range = ranges[extWorker]; stopIndex = extWorker + 1 == num ? toExclusive : Math.Min(toExclusive, fromInclusive + (extWorker + 1) * step); int stolen = -1; do { do { long old; StealValue64 val = new StealValue64(); old = sixtyfour ? range.V64.Value : Interlocked.CompareExchange(ref range.V64.Value, 0, 0); val.Value = old; if (val.Actual >= stopIndex - val.Stolen - 2) { goto next; } stolen = (val.Stolen += 1); if (Interlocked.CompareExchange(ref range.V64.Value, val.Value, old) == old) { break; } } while (true); stolen = stopIndex - stolen; if (stolen > range.V64.Actual) { local = body(stolen, state, local); } else { break; } } while (true); next: continue; } } finally { localFinally(local); } }; InitTasks(tasks, num, workerMethod, parallelOptions); try { Task.WaitAll(tasks); } catch { HandleExceptions(tasks, infos); } return(new ParallelLoopResult(infos.LowestBreakIteration, !(infos.IsStopped || infos.IsExceptional))); }
static ParallelLoopResult ForEach <TSource, TLocal> (Func <int, IList <IEnumerator <TSource> > > enumerable, ParallelOptions options, Func <TLocal> init, Func <TSource, ParallelLoopState, TLocal, TLocal> action, Action <TLocal> destruct) { if (enumerable == null) { throw new ArgumentNullException("source"); } if (options == null) { throw new ArgumentNullException("options"); } if (action == null) { throw new ArgumentNullException("action"); } if (init == null) { throw new ArgumentNullException("init"); } if (destruct == null) { throw new ArgumentNullException("destruct"); } int num = Math.Min(GetBestWorkerNumber(options.TaskScheduler), options != null && options.MaxDegreeOfParallelism != -1 ? options.MaxDegreeOfParallelism : int.MaxValue); Task[] tasks = new Task[num]; ParallelLoopState.ExternalInfos infos = new ParallelLoopState.ExternalInfos(); SimpleConcurrentBag <TSource> bag = new SimpleConcurrentBag <TSource> (num); const int bagCount = 5; IList <IEnumerator <TSource> > slices = enumerable(num); int sliceIndex = -1; Action workerMethod = delegate { IEnumerator <TSource> slice = slices[Interlocked.Increment(ref sliceIndex)]; TLocal local = init(); ParallelLoopState state = new ParallelLoopState(infos); int workIndex = bag.GetNextIndex(); CancellationToken token = options.CancellationToken; try { bool cont = true; TSource element; while (cont) { if (infos.IsStopped || infos.IsBroken.Value) { return; } token.ThrowIfCancellationRequested(); for (int i = 0; i < bagCount && (cont = slice.MoveNext()); i++) { bag.Add(workIndex, slice.Current); } for (int i = 0; i < bagCount && bag.TryTake(workIndex, out element); i++) { if (infos.IsStopped) { return; } token.ThrowIfCancellationRequested(); local = action(element, state, local); } } while (bag.TrySteal(workIndex, out element)) { token.ThrowIfCancellationRequested(); local = action(element, state, local); if (infos.IsStopped || infos.IsBroken.Value) { return; } } } finally { destruct(local); } }; InitTasks(tasks, num, workerMethod, options); try { Task.WaitAll(tasks); } catch { HandleExceptions(tasks, infos); } return(new ParallelLoopResult(infos.LowestBreakIteration, !(infos.IsStopped || infos.IsExceptional))); }
public static ParallelLoopResult For <TLocal> (int from, int to, ParallelOptions options, Func <TLocal> init, Func <int, ParallelLoopState, TLocal, TLocal> action, Action <TLocal> destruct) { if (action == null) { throw new ArgumentNullException("action"); } if (init == null) { throw new ArgumentNullException("localInit"); } if (destruct == null) { throw new ArgumentNullException("localFinally"); } if (options == null) { throw new ArgumentNullException("options"); } if (from >= to) { return(new ParallelLoopResult(null, true)); } // Number of task to be launched (normally == Env.ProcessorCount) int step; int num = GetBestWorkerNumber(from, to, options, out step); Task[] tasks = new Task [num]; StealRange[] ranges = new StealRange[num]; for (int i = 0; i < num; i++) { ranges[i] = new StealRange(from, i, step); } ParallelLoopState.ExternalInfos infos = new ParallelLoopState.ExternalInfos(); int currentIndex = -1; Action workerMethod = delegate { int localWorker = Interlocked.Increment(ref currentIndex); StealRange range = ranges[localWorker]; int index = range.Actual; int stopIndex = localWorker + 1 == num ? to : Math.Min(to, index + step); TLocal local = init(); ParallelLoopState state = new ParallelLoopState(infos); CancellationToken token = options.CancellationToken; try { for (int i = index; i < stopIndex; range.Actual = ++i) { if (infos.IsStopped) { return; } token.ThrowIfCancellationRequested(); if (infos.LowestBreakIteration != null && infos.LowestBreakIteration > i) { return; } state.CurrentIteration = i; local = action(i, state, local); if (i >= stopIndex - range.Stolen) { break; } } // Try to steal from our right neighbor (cyclic) int len = num + localWorker; for (int sIndex = localWorker + 1; sIndex < len; ++sIndex) { int extWorker = sIndex % num; range = ranges[extWorker]; stopIndex = extWorker + 1 == num ? to : Math.Min(to, from + (extWorker + 1) * step); int stolen; do { stolen = range.Stolen; if (stopIndex - stolen > range.Actual) { goto next; } } while (Interlocked.CompareExchange(ref range.Stolen, stolen + 1, stolen) != stolen); stolen = stopIndex - stolen - 1; if (stolen > range.Actual) { local = action(stolen, state, local); } next: continue; } } finally { destruct(local); } }; InitTasks(tasks, num, workerMethod, options); try { Task.WaitAll(tasks); } catch { HandleExceptions(tasks, infos); } return(new ParallelLoopResult(infos.LowestBreakIteration, !(infos.IsStopped || infos.IsExceptional))); }