Description
Description
I am trying to test some codes/logics that basically testing the speed or perf of adding item to a List and then this code is wrapped inside a Task.Run, then the code in the Task.Run is awaited, added to a List, and then all of the tasks are awaited into a single Task.WhenAll.
Unfortunately I got exceptions intermittently thrown when adding item to a List.
Reproduction Steps
This is the code to reproduce on my gist at GitHub:
https://gist.github.com/eriawan/65d38acbdd3649f394d6eff0f1faa434
Just run the console project and the code files in that Gist.
This method is excerpted from the Gist, note the line that add the item to randomResult, this is the point that throws exception.
public static async Task TaskWhenAll_ProcessIterateFor()
{
List<Task> tasklist = new List<Task>();
List<Double> randomResult = new List<Double>();
for (int i = 1; i <= 50; i++)
{
tasklist.Add(Task.Run(() =>
{
var iterationResult = ProcessIterateFor(60);
iterationResult.ForEach((resultItem) => randomResult.Add(resultItem));
}));
}
await Task.WhenAll(tasklist);
}
Expected behavior
I expect that it should not be having/throwing error/exception at all.
Actual behavior
This exception is intermittently thrown:
- Type: System.ArgumentException
- Exceptio message:
Source array was not long enough. Check the source index, length, and the array's lower bounds. (Parameter 'sourceArray')
- Stack trace: (with "Just my code" setting disabled)
at System.Array.CopyImpl(Array sourceArray, Int32 sourceIndex, Array destinationArray, Int32 destinationIndex, Int32 length, Boolean reliable) in /_/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs:line 45
at System.Collections.Generic.List`1.set_Capacity(Int32 value) in /_/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/List.cs:line 114
at System.Collections.Generic.List`1.AddWithResize(T item) in /_/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/List.cs:line 218
at DiskusiTestArrayExceptionDiTaskWhenAll.MiscelanousComputeProcessSimplified.<>c__DisplayClass1_0.<TaskWhenAll_ProcessIterateFor>b__1(Double resultItem) in D:\github-repo-large\belajar-dotnet-bahasa\source\DiskusiTestArrayExceptionDiTaskWhenAll\MiscelanousComputeProcessSimplified.cs:line 35
at System.Collections.Generic.List`1.ForEach(Action`1 action) in /_/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/List.cs:line 616
at DiskusiTestArrayExceptionDiTaskWhenAll.MiscelanousComputeProcessSimplified.<>c__DisplayClass1_0.<TaskWhenAll_ProcessIterateFor>b__0() in D:\github-repo-large\belajar-dotnet-bahasa\source\DiskusiTestArrayExceptionDiTaskWhenAll\MiscelanousComputeProcessSimplified.cs:line 35
at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state) in /_/src/libraries/System.Private.CoreLib/src/System/Threading/ExecutionContext.cs:line 264
--- End of stack trace from previous location ---
Screenshots:
When exception caught:
and this is the debugged source code of that:
Regression?
Not that I can tell whether this is regression or not, but I can reproduce this on .NET Framework 4.8, .NET 8.0 and .NET 9.0, although the stack trace is for .NET 8.0 and .NET 9.0
Known Workarounds
If I use ConcurrentBag within the code that add the item, the problem seems almost gone, although the perf is somehow slower.
But is this the only way? Just for curiosity, I have tried to use lock, CMIIW. But using lock still doesn't solve the problem and the perf is worse than without lock.
Any correction/feedback appreciated, thanks in advance!
Configuration
.NET SDK I use:
- .NET SDK 9.0.300, Runtime: 9.0.5
- .NET SDK 8.0.410, Runtime 8.0.16
Visual Studio used: VS 2022 17.14.4
Other information
If I use Parallel.ForAsync, the problem is gone. But I may have less scenario to test, as I only have 50 times of loop.