Upgraded Nuget packages. Added a new For function for executing a for loop in parallel. Added unit tests. Bumped package version to 1.0.2

This commit is contained in:
MattMo 2024-11-14 19:29:45 -08:00
parent 779ceee2c1
commit 58e2a809a0
4 changed files with 99 additions and 6 deletions

View File

@ -11,14 +11,14 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.11.0" /> <PackageReference Include="FluentAssertions" Version="6.12.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="xunit" Version="2.4.2" /> <PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> <PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="3.2.0"> <PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>

View File

@ -147,5 +147,41 @@ namespace MontoyaTech.Parallel.Net.Tests
results.Should().Be(5); results.Should().Be(5);
} }
[Fact]
public void Parallel_For_Odd_Should_Work()
{
var indexes = new List<long>();
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<long>();
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}");
}
} }
} }

View File

@ -14,7 +14,7 @@
<PackageIcon>Logo_Symbol_Black_Outline.png</PackageIcon> <PackageIcon>Logo_Symbol_Black_Outline.png</PackageIcon>
<RepositoryUrl>https://code.montoyatech.com/MontoyaTech/Parallel.Net</RepositoryUrl> <RepositoryUrl>https://code.montoyatech.com/MontoyaTech/Parallel.Net</RepositoryUrl>
<GenerateDocumentationFile>True</GenerateDocumentationFile> <GenerateDocumentationFile>True</GenerateDocumentationFile>
<Version>1.0.1</Version> <Version>1.0.2</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -216,6 +216,63 @@ namespace MontoyaTech.Parallel.Net
throw tasks[i].ThrownException; throw tasks[i].ThrownException;
} }
/// <summary>
/// Runs an action for loop in parallel where index greaterthanorequal start and index lessthan end.
/// </summary>
/// <param name="start">The starting index.</param>
/// <param name="end">The ending index, index is always less than this value.</param>
/// <param name="action">The paramter is the index from the loop.</param>
/// <param name="maxTasks">The max number of tasks to run in parallel. If null the number of available cores is used. Default is null.</param>
/// <param name="timeout">If set this function will return early if the tieout is reached even if the tasks are not done. Default is null.</param>
/// <param name="throwOnTimeout">If true an exception will be thrown if a timeout is reached is one was set. Default is true.</param>
/// <param name="bubbleExceptions">If true, rethrows any exceptions from the running tasks. Default is true.</param>
public static void For(long start, long end, Action<long> 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<ParallelTask<(long chunkStart, long chunkEnd)>>();
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;
}
/// <summary> /// <summary>
/// Runs a list of tasks and waits until they have been completed or failed and returns the results. /// Runs a list of tasks and waits until they have been completed or failed and returns the results.
/// </summary> /// </summary>