C# Task.WhenAll用于ValueTask
是否存在任务的等价物。当所有接受任务时ValueTask 我可以使用C# Task.WhenAll用于ValueTask,c#,task-parallel-library,valuetask,C#,Task Parallel Library,Valuetask,是否存在任务的等价物。当所有接受任务时ValueTask 我可以使用 Task.WhenAll(tasks.Select(t => t.AsTask())) 如果他们都在包装一个任务,这将很好,但它会强制将任务对象无效地分配给realValueTask,根据设计,否: 方法可能会返回此值类型的实例,因为它们的操作结果可能会同步可用,并且该方法的调用频率很高,以至于为每个调用分配新任务的成本很高 例如,考虑一种方法,它可以返回一个“代码>任务/,将缓存任务作为一个公共结果或 ValueT
Task.WhenAll(tasks.Select(t => t.AsTask()))
如果他们都在包装一个任务
,这将很好,但它会强制将任务
对象无效地分配给realValueTask
,根据设计,否:
方法可能会返回此值类型的实例,因为它们的操作结果可能会同步可用,并且该方法的调用频率很高,以至于为每个调用分配新任务的成本很高
例如,考虑一种方法,它可以返回一个“代码>任务/<代码>,将缓存任务作为一个公共结果或<代码> ValueTeaS/<代码>。如果结果的使用者希望将其用作
任务
,例如使用Task.WhenAll
和Task.whenay
,则首先需要使用AsTask
将值任务转换为任务
,这导致了一种分配,如果一开始就使用了缓存的任务
,这种分配本来是可以避免的
正如@stuartd所指出的,它不受设计的支持,我必须手动实现它:
public static async Task<IReadOnlyCollection<T>> WhenAll<T>(this IEnumerable<ValueTask<T>> tasks)
{
var results = new List<T>();
var toAwait = new List<Task<T>>();
foreach (var valueTask in tasks)
{
if (valueTask.IsCompletedSuccessfully)
results.Add(valueTask.Result);
else
toAwait.Add(valueTask.AsTask());
}
results.AddRange(await Task.WhenAll(toAwait).ConfigureAwait(false));
return results;
}
公共静态异步任务WhenAll(此IEnumerable任务)
{
var results=新列表();
var toAwait=新列表();
foreach(任务中的var valueTask)
{
if(valueTask.IsCompletedSuccessfully)
results.Add(valueTask.Result);
其他的
添加(valueTask.AsTask());
}
results.AddRange(wait Task.WhenAll(toAwait).configurewait(false));
返回结果;
}
当然,这只会在高吞吐量和高数量的ValueTask
中有所帮助,因为它会增加一些其他开销
注:正如@StephenCleary所指出的,这不会将顺序保持为任务。当所有保持时,如果需要,可以很容易地进行更改以实现它。尝试进行一些优化,结果以正确的顺序返回,并进行正确的异常处理
public static ValueTask<T[]> WhenAll<T>(IEnumerable<ValueTask<T>> tasks)
{
var list = tasks.ToList();
var length = list.Count;
var result = new T[length];
var i = 0;
for (; i < length; i ++)
{
if (list[i].IsCompletedSuccessfully)
{
result[i] = list[i].Result;
}
else
{
return WhenAllAsync();
}
}
return new ValueTask<T[]>(result);
async ValueTask<T[]> WhenAllAsync()
{
for (; i < length; i ++)
{
try
{
result[i] = await list[i];
}
catch
{
for (i ++; i < length; i ++)
{
try
{
await list[i];
}
catch
{
// ignored
}
}
throw;
}
}
return result;
}
}
publicstaticvaluetask whalll(IEnumerable任务)
{
var list=tasks.ToList();
var length=list.Count;
var结果=新的T[长度];
var i=0;
对于(;i
我正在使用此扩展方法:
internal static class ValueTaskExtensions
{
public static Task WhenAll(this IEnumerable<ValueTask> tasks)
{
return Task.WhenAll(tasks.Select(v => v.AsTask()));
}
}
内部静态类ValueTaskExtensions
{
公共静态任务WhenAll(此IEnumerable任务)
{
返回Task.WhenAll(tasks.Select(v=>v.AsTask());
}
}
除非我遗漏了什么,否则我们应该能够在循环中等待所有任务:
public static async ValueTask<T[]> WhenAll<T>(params ValueTask<T>[] tasks)
{
// Argument validations omitted
var results = new T[tasks.Length];
for (var i = 0; i < tasks.Length; i++)
results[i] = await tasks[i].ConfigureAwait(false);
return results;
}
我们直接抛出第一个异常,因为用aggregateeexception
包装它不是ValueTask
的事情
:
…如果在任务操作期间发生异常,或者如果任务已取消,则Result属性不会返回值。相反,尝试访问属性值会引发AggregateException异常
:
如果此ValueTask出现故障,则此属性会引发异常。抛出的异常未包装在AggregateException中
但是如果我们确实希望我们的whalll
方法抛出一个包含所有抛出异常的aggregateeexception
,我们可以:
public static async ValueTask<T[]> WhenAll<T>(params ValueTask<T>[] tasks)
{
Exception? exception = null;
var results = new T[tasks.Length];
for (var i = 0; i < tasks.Length; i++)
try
{
results[i] = await tasks[i].ConfigureAwait(false);
}
catch (Exception ex)
{
// Remember the first exception, swallow the rest
exception ??= ex;
}
return exception is null
? results
: throw exception;
}
public static async ValueTask<T[]> WhenAll<T>(params ValueTask<T>[] tasks)
{
// We don't allocate the list if no task throws
List<Exception>? exceptions = null;
var results = new T[tasks.Length];
for (var i = 0; i < tasks.Length; i++)
try
{
results[i] = await tasks[i].ConfigureAwait(false);
}
catch (Exception ex)
{
exceptions ??= new List<Exception>(tasks.Length);
exceptions.Add(ex);
}
return exceptions is null
? results
: throw new AggregateException(exceptions);
}
public静态异步ValueTask whalll(参数ValueTask[]tasks)
{
//如果没有任务抛出,我们不会分配列表
列表?异常=空;
var结果=新的T[tasks.Length];
对于(var i=0;i
Task.WhenAll(tasks.Where(t=>!t.IsCompletedSuccessfully)。Select(t=>t.AsTask())
结果的收集顺序也可能与原始值任务不同。@StephenCleary考虑到了这一点,但我并不是说它可以精确替代Task.WhenAll
,不过,它很容易修复。讽刺的是,使用ValueTask
来减少分配,但无论如何都要借助分配来使用它。虽然知道它是设计出来的很好,但我看不出有任何理由。除了自己实现任务外,还不清楚什么是缓存任务的简单方法。HadTask。WhenAny
和Task。WhenAll
对ValueTask
s有重载,事情会简单得多。在考虑了@marc gravell在该链接和其他链接中提到的注意事项后,这个答案@Nathan我开始在我的API中使用ValueTask。但是仍然没有类似于任务的东西。Whalll
…@stuartd,同意-只是指出围绕主题的文档是。。。老化:)这不能满足调用AsTas的问题