-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Closed
Labels
api-approvedAPI was approved in API review, it can be implementedAPI was approved in API review, it can be implementedarea-System.Threading.Tasks
Milestone
Description
There are situations where a asynchronous parallel foreach is needed instead of the already existing Parallel.ForEach() and while it's not rocket science to implement yourself I think it would be a nice feature to be implemented in corefx. This should be seen as the asynchronous alternative to the existing Parallel.ForEach(). And should take IEnumerable<T> as well as IAsyncEnumerable<T>.
There are examples on this issue already here is one: https://devblogs.microsoft.com/pfxteam/implementing-a-simple-foreachasync-part-2/
/// <summary>
/// Executes a foreach asynchronously.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source">The source.</param>
/// <param name="dop">The degrees of parallelism.</param>
/// <param name="body">The body.</param>
/// <returns></returns>
public static Task ForEachAsync<T>(this IEnumerable<T> source, int dop, Func<T, Task> body)
{
return Task.WhenAll(
from partition in System.Collections.Concurrent.Partitioner.Create(source).GetPartitions(dop)
select Task.Run(async delegate
{
using (partition)
{
while (partition.MoveNext())
await body(partition.Current);
}
}));
}Proposed API
namespace System.Threading.Tasks
{
public static class Parallel
{
public static Task ForEachAsync<T>(IAsyncEnumerable<T> source, Func<T, ValuteTask> body);
public static Task ForEachAsync<T>(IAsyncEnumerable<T> source, ParallelOptions parallelOptions, Func<T, CancellationToken, ValueTask> body);
public static Task ForEachAsync<T>(IEnumerable<T> source, Func<T, ValuteTask> body);
public static Task ForEachAsync<T>(IEnumerable<T> source, ParallelOptions parallelOptions, Func<T, CancellationToken, ValuteTask> body);
}
}Closed questions
While I consider these questions to be closed feel free to continue discussion on them.
- Should DOP be automatic or manual at all times?
DOP Will have a default, just like parallel linq. However documentation should make it clear that this default can be wrong in various cases. Such as detailed by @GSPP in various comments. - Should it accept IEnumerable?
Yes it should - Should the partitioner concept be supported at all?
No we should ensure the specified DOP is adhered to exactly. Creating batches can lead to a smaller than expected effective DOP. - Should we process the source items sequentially or make no attempt?
By default we should try to process source items sequentially to improve data locality. Reason being: HDD read performance will increase drastically. Furthermore cache usage will be improved. It should however be made very clear to people in docs, that the order cannot be guaranteed - Should there be an alternative to Parallel.For?
There should be one but I'll leave that outside of this issue. - Should the funcs return ValueTask instead of Task?
Yes ValueTask is more accomodating than Task (cheaper to convert Task to ValueTask than the other way arround). Furthermore it should result in a peformance increase. - Should TaskCreationOptions.DenyChildAttach be specified?
Yes this should be specified see This comment for more info - Should there be an overload without CancellationToken in the Func?
No there shouldn't be. It would have little to no benefit, but would cause a lot of implications (ambiguity, analysis becomes harder)
svick, BrammyS, jochemth7, HalidCisse, GSPP and 57 moreGSPP, Misiu, Mrxx99, PureKrome, dgioulakis and 1 moreMrxx99, PureKrome, VladimirReshetnikov, dgioulakis and botinko
Metadata
Metadata
Assignees
Labels
api-approvedAPI was approved in API review, it can be implementedAPI was approved in API review, it can be implementedarea-System.Threading.Tasks