/// <summary> /// Initializes a new instance of the <see cref="DrawingProgress"/> struct. /// </summary> /// <param name="operationType">Specifies the type of the drawing operation.</param> /// <param name="maximumValue">The maximum value.</param> /// <param name="currentValue">The current value.</param> public DrawingProgress(DrawingOperation operationType, int maximumValue, int currentValue) { if (!operationType.IsDefined()) { throw new ArgumentOutOfRangeException(nameof(operationType), PublicResources.EnumOutOfRange(operationType)); } if (maximumValue < 0) { throw new ArgumentOutOfRangeException(nameof(maximumValue), PublicResources.ArgumentMustBeGreaterThanOrEqualTo(0)); } if ((uint)currentValue > (uint)maximumValue) { throw new ArgumentOutOfRangeException(nameof(currentValue), PublicResources.ArgumentMustBeBetween(0, maximumValue)); } OperationType = operationType; MaximumValue = maximumValue; CurrentValue = currentValue; }
internal static void For(IAsyncContext context, DrawingOperation operation, int fromInclusive, int toExclusive, Action <int> body) { #region Local Methods #if !NET35 void DoWorkWithProgress(int y) { body.Invoke(y); context.Progress.Increment(); } void DoWorkWithCancellation(int y, ParallelLoopState state) { if (context.IsCancellationRequested) { state.Stop(); return; } body.Invoke(y); } void DoWorkWithCancellationAndProgress(int y, ParallelLoopState state) { if (context.IsCancellationRequested) { state.Stop(); return; } body.Invoke(y); context.Progress !.Increment(); } #endif #endregion int count = toExclusive - fromInclusive; context.Progress?.New(operation, Math.Max(count, 0)); // a single iteration: invoke once if (count <= 1) { if (count < 1 || context.IsCancellationRequested) { return; } body.Invoke(fromInclusive); context.Progress?.Increment(); return; } // single core or no parallelism: sequential invoke if (CoreCount == 1 || context.MaxDegreeOfParallelism == 1) { for (int i = fromInclusive; i < toExclusive; i++) { if (context.IsCancellationRequested) { return; } body.Invoke(i); context.Progress?.Increment(); } return; } #if NET35 int busyCount = 0; Exception?error = null; int maxThreads = context.MaxDegreeOfParallelism <= 0 ? CoreCount : context.MaxDegreeOfParallelism; int rangeSize = count / maxThreads; // we have enough cores/degree for each iteration if (rangeSize <= 1) { for (int i = fromInclusive; i < toExclusive; i++) { // not queuing more tasks than the limit while (busyCount >= maxThreads && error == null) { Thread.Sleep(0); } if (error != null || context.IsCancellationRequested) { break; } Interlocked.Increment(ref busyCount); int value = i; ThreadPool.QueueUserWorkItem(_ => { try { body.Invoke(value); context.Progress?.Increment(); } catch (Exception e) { Interlocked.CompareExchange(ref error, e, null); } finally { // ReSharper disable once AccessToModifiedClosure - intended, both outside and inside changes matter Interlocked.Decrement(ref busyCount); } }); } } // we merge some iterations to be processed by the same core else { var ranges = CreateRanges(fromInclusive, toExclusive, rangeSize); foreach (var range in ranges) { // not queuing more tasks than the number of cores while (busyCount >= maxThreads && error == null) { Thread.Sleep(0); } if (error != null || context.IsCancellationRequested) { break; } Interlocked.Increment(ref busyCount); ThreadPool.QueueUserWorkItem(_ => { try { for (int i = range.From; i < range.To; i++) { if (context.IsCancellationRequested) { return; } body.Invoke(i); context.Progress?.Increment(); } } catch (Exception e) { Interlocked.CompareExchange(ref error, e, null); } finally { // ReSharper disable once AccessToModifiedClosure - intended, both outside and inside changes matter Interlocked.Decrement(ref busyCount); } }); } } // waiting until every task is finished while (busyCount > 0) { Thread.Sleep(0); } if (error != null) { ExceptionDispatchInfo.Capture(error).Throw(); } #else Action <int, ParallelLoopState>?bodyWithState = null; Action <int>?simpleBody = null; if (context.CanBeCanceled) { bodyWithState = context.Progress == null ? DoWorkWithCancellation : DoWorkWithCancellationAndProgress; } else { simpleBody = context.Progress == null ? body : DoWorkWithProgress; } int rangeSize; ParallelOptions options; if (context.MaxDegreeOfParallelism <= 0) { // we allow a bit more fine resolution than the actual core counts rangeSize = (count / CoreCount) >> 2; options = defaultParallelOptions; } else { // we allow a bit more fine resolution than the specified degree rangeSize = (count / context.MaxDegreeOfParallelism) >> 2; options = new ParallelOptions { MaxDegreeOfParallelism = context.MaxDegreeOfParallelism }; } // we have enough cores/degree for each iteration if (rangeSize <= 1) { if (bodyWithState != null) { Parallel.For(fromInclusive, toExclusive, options, bodyWithState); } else { Parallel.For(fromInclusive, toExclusive, options, simpleBody !); } return; } // we merge some iterations to be processed by the same core var partitions = Partitioner.Create(fromInclusive, toExclusive, rangeSize); if (bodyWithState != null) { Parallel.ForEach(partitions, options, (range, state) => { (int from, int to) = range; for (int i = from; i < to; i++) { bodyWithState.Invoke(i, state); if (state.IsStopped) { return; } } }); return; } Parallel.ForEach(partitions, options, range => { (int from, int to) = range; for (int i = from; i < to; i++) { simpleBody !.Invoke(i); }
public void New(DrawingOperation operationType, int maximumValue, int currentValue) => Report(new DrawingProgress(operationType, maximumValue, currentValue));