在审查承包商的工作时,我遇到了以下扩展方法:
public static class TaskExtensions
{
public static async Task<IEnumerable<TResult>> WhenAllSynchronousExecution<TResult>(this IEnumerable<Task<TResult>> tasks, CancellationToken token = default(CancellationToken))
{
var results = new List<TResult>();
var exceptions = new List<Exception>();
foreach (var task in tasks)
{
if (task == null)
{
throw new ArgumentNullException(nameof(tasks));
}
if (token.IsCancellationRequested) {
exceptions.Add(new OperationCanceledException("Tasks collection cancelled", token));
break;
}
try
{
results.Add(await task);
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
if (exceptions.Any()) {
throw new AggregateException(exceptions);
}
return results;
}
}
这看起来正是 task.whenall&lt; tresult&gt;
引用这些话:
何时呼叫(ienumerable&lt; task&gt;)方法不会阻止调用线程。但是,对返回结果属性的调用确实会阻止调用线程。
如果所提供的任何任务以故障状态完成,则返回的任务也将在故障状态下完成,在此,其例外将包含从每个提供的任务中的一组未包装异常的聚合。
如果未提供的任务都没有故障,但其中至少一个被取消,则返回的任务将以取消状态结束。
如果任何任务都没有故障且没有取消任务,则结果任务将以rantocompletion状态结束。返回任务的任务属性将设置为一个数组,该数组包含所提供任务的所有结果,以与提供的顺序相同(例如,如果输入任务数组包含T1,T2,T3,则输出任务的任务,则输出任务的任务.Result属性将返回一个tresult [],其中arr [0] == t1。result,arr [1] == t2.Result,and arr [2] == t3.Result)。
如果任务参数不包含任何任务,则返回的任务将在返回给呼叫者之前立即过渡到rantocoltetion状态。返回的tresult []将是0个元素的数组。
因此,对我来说,似乎他们已经重新创建了一种现有方法。唯一的区别似乎是检查 concellationToken
的检查,但是我看不到Mych添加的值(代码中未使用)。也许这是一种扩展方法,但也没有在代码中使用(称为 var task = taskextensions.whenallsynchronyousexecution(tasks);
)
,但该同事非常“敏感”。所以我需要挑出战斗。那么,这只是任务的自定义,微薄的娱乐。
When reviewing work by a contractor, I came upon the following extension method:
public static class TaskExtensions
{
public static async Task<IEnumerable<TResult>> WhenAllSynchronousExecution<TResult>(this IEnumerable<Task<TResult>> tasks, CancellationToken token = default(CancellationToken))
{
var results = new List<TResult>();
var exceptions = new List<Exception>();
foreach (var task in tasks)
{
if (task == null)
{
throw new ArgumentNullException(nameof(tasks));
}
if (token.IsCancellationRequested) {
exceptions.Add(new OperationCanceledException("Tasks collection cancelled", token));
break;
}
try
{
results.Add(await task);
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
if (exceptions.Any()) {
throw new AggregateException(exceptions);
}
return results;
}
}
To this looks to be exactly what Task.WhenAll<TResult>
already does. quote the remarks:
The call to WhenAll(IEnumerable<Task>) method does not block the calling thread. However, a call to the returned Result property does block the calling thread.
If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state, where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks.
If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state.
If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state. The Task.Result property of the returned task will be set to an array containing all of the results of the supplied tasks in the same order as they were provided (e.g. if the input tasks array contained t1, t2, t3, the output task's Task.Result property will return an TResult[] where arr[0] == t1.Result, arr[1] == t2.Result, and arr[2] == t3.Result).
If the tasks argument contains no tasks, the returned task will immediately transition to a RanToCompletion state before it's returned to the caller. The returned TResult[] will be an array of 0 elements.
So to me it seems they've recreated an existing method. The only difference seems to be the checking of the CancellationToken
, but I don't see mych added value there (it's not used in the code). And maybe that it's an extension method, but that's also not used in the code (it's called like var task = TaskExtensions.WhenAllSynchronousExecution(tasks);
)
But this colleague is quite "sensitive". So I need to pick my battles. So is this just a custom,meagre recreation of Task.WhenAll<TResult>
?
发布评论
评论(2)
不,
task.whenall
和wheralsynchronousexecution
。这是其中的一些:task.whenall
立即实现iEnumerable&lt; tast&lt; tresult&gt;&gt;
序列,通过枚举并将任务存储在数组中。这样可以确保所有任务都启动并运行,然后再将其连续延长。当AllsynChronouseXecution
不执行此操作,因此根据如何实现了枚举序列,它可能会在上一个任务完成(顺序)完成后开始每个任务。task.whenall
不接受concellationToken
,因此,完成后,您可以100%确定 all完全的。与waitasync
方法( .NET >直接传播异常,作为 Innerexceptions href =“ https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.tasks.tasks.task.task.task.exception” rel =“ nofollow noreferrer”>task.exception.exception
属性。当allsynchronousexecution
将异常包装在附加gencregateException
中。因此,如果不重新考虑异常处理代码,则不能在两个API之间切换。task.whenall
完成为取消
当任务都不是故障
时,至少一个是取消
。当AllsynChronouseXecution
始终将取消作为错误传播。当allsynchronousexecution
在其内部等待
点中捕获synchronizationContext
,因此如果呼叫者上的块。task.whenall
同步检查null
任务实例。当allsynchronousexecution
在等待每个任务的循环期间进行检查。校正:实际上是集成的
concellationToken
是一个有用的功能,它已经在github上请求。Nope, there are quite a lot of differences between the
Task.WhenAll
and theWhenAllSynchronousExecution
. Here are some of them:Task.WhenAll
materializes immediately theIEnumerable<Task<TResult>>
sequence, by enumerating it and storing the tasks in an array. This ensures that all the tasks are up and running, before attaching continuations on them. TheWhenAllSynchronousExecution
doesn't do that, so it could potentially start each task after the completion of the previous task (sequentially), depending on how the enumerable sequence is implemented.TheTask.WhenAll
doesn't take aCancellationToken
, so when it completes you can be 100% sure that all the tasks have completed. It is always possible to fire-and-forget aTask.WhenAll
task, just like any otherTask
, with theWaitAsync
method (.NET 6), so there is no reason to bake this functionality in every async method.Task.WhenAll
propagates the exceptions directly, as theInnerExceptions
of theTask.Exception
property. TheWhenAllSynchronousExecution
wraps the exceptions in an additionalAggregateException
. So you can't switch between the two APIs without rethinking your exception-handling code.Task.WhenAll
completes asCanceled
when none of the tasks isFaulted
, and at least one isCanceled
. TheWhenAllSynchronousExecution
propagates the cancellation as error, always.WhenAllSynchronousExecution
captures theSynchronizationContext
in its internalawait
points, so it might cause a deadlock if the caller blocks on async code.Task.WhenAll
checks fornull
task instances synchronously. TheWhenAllSynchronousExecution
does the check during the loop that awaits each task.Correction: Actually the integrated
CancellationToken
is a useful feature, that has been requested on GitHub.我认为弄清楚此方法是否存在任何理由的关键是确认在调用该方法之前是否启动任务。
〜当alleSequention
时。无论如何,我都会反对将此方法作为扩展方法:
I think the key to figuring out if this method has any reason to exist is to confirm if the tasks are started before the method is called.
~WhenAllSequential
.In any case, I would argue against having this method as an extension method:
WhenAll
is not an extension method.