diff --git a/Parallel.Net.Tests/Parallel.Net.Tests.csproj b/Parallel.Net.Tests/Parallel.Net.Tests.csproj index d8ba1ff..aa54567 100644 --- a/Parallel.Net.Tests/Parallel.Net.Tests.csproj +++ b/Parallel.Net.Tests/Parallel.Net.Tests.csproj @@ -11,14 +11,14 @@ - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/Parallel.Net.Tests/ParallelTests.cs b/Parallel.Net.Tests/ParallelTests.cs index 65a34e3..006f650 100644 --- a/Parallel.Net.Tests/ParallelTests.cs +++ b/Parallel.Net.Tests/ParallelTests.cs @@ -147,5 +147,41 @@ namespace MontoyaTech.Parallel.Net.Tests results.Should().Be(5); } + + [Fact] + public void Parallel_For_Odd_Should_Work() + { + var indexes = new List(); + + ParallelTask.For(0, 3, index => + { + lock (indexes) + indexes.Add(index); + }, 2); + + indexes.Count.Should().Be(3); + + for (long i = 0; i < 3; i++) + if (!indexes.Contains(i)) + throw new Exception($"Test failed, missing index: {i}"); + } + + [Fact] + public void Parallel_For_Even_Should_Work() + { + var indexes = new List(); + + ParallelTask.For(0, 4, index => + { + lock (indexes) + indexes.Add(index); + }, 2); + + indexes.Count.Should().Be(4); + + for (long i = 0; i < 4; i++) + if (!indexes.Contains(i)) + throw new Exception($"Test failed, missing index: {i}"); + } } } diff --git a/Parallel.Net/Parallel.Net.csproj b/Parallel.Net/Parallel.Net.csproj index 3bc29db..abcc025 100644 --- a/Parallel.Net/Parallel.Net.csproj +++ b/Parallel.Net/Parallel.Net.csproj @@ -14,7 +14,7 @@ Logo_Symbol_Black_Outline.png https://code.montoyatech.com/MontoyaTech/Parallel.Net True - 1.0.1 + 1.0.2 diff --git a/Parallel.Net/ParallelTask.cs b/Parallel.Net/ParallelTask.cs index 60a6271..1c3745c 100644 --- a/Parallel.Net/ParallelTask.cs +++ b/Parallel.Net/ParallelTask.cs @@ -216,6 +216,63 @@ namespace MontoyaTech.Parallel.Net throw tasks[i].ThrownException; } + /// + /// Runs an action for loop in parallel where index greaterthanorequal start and index lessthan end. + /// + /// The starting index. + /// The ending index, index is always less than this value. + /// The paramter is the index from the loop. + /// The max number of tasks to run in parallel. If null the number of available cores is used. Default is null. + /// If set this function will return early if the tieout is reached even if the tasks are not done. Default is null. + /// If true an exception will be thrown if a timeout is reached is one was set. Default is true. + /// If true, rethrows any exceptions from the running tasks. Default is true. + public static void For(long start, long end, Action action, int? maxTasks = null, TimeSpan? timeout = null, bool throwOnTimeout = true, bool bubbleExceptions = true) + { + if (start == 0 && end == start) + return; + else if (start >= end) + return; + + var tasks = new List>(); + + if (!maxTasks.HasValue) + maxTasks = Environment.ProcessorCount; + else if (maxTasks <= 0) + maxTasks = 1; + + long itemsPerChunk = ((end - start) / (long)maxTasks.Value) + 1; + + long index = start; + for (int i = 0; i < maxTasks.Value; i++) + { + var chunkStart = index; + var chunkEnd = index + itemsPerChunk; + index += itemsPerChunk; + + if (chunkEnd > end) + chunkEnd = end; + + tasks.Add(new ParallelTask<(long chunkStart, long chunkEnd)>(state => + { + for (long i = state.chunkStart; i < state.chunkEnd; i++) + action(i); + }, (chunkStart, chunkEnd))); + + if (index >= end) + break; + } + + if (tasks.Count == 0) + return; + + WhenAll(tasks, timeout, throwOnTimeout); + + if (bubbleExceptions) + for (int i = 0; i < tasks.Count; i++) + if (tasks[i].Failed && tasks[i].ThrownException != null) + throw tasks[i].ThrownException; + } + /// /// Runs a list of tasks and waits until they have been completed or failed and returns the results. ///