Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/288.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Task.WhenAll用于ValueTask_C#_Task Parallel Library_Valuetask - Fatal编程技术网

C# Task.WhenAll用于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

是否存在任务的等价物。当所有接受任务时ValueTask

我可以使用

Task.WhenAll(tasks.Select(t => t.AsTask()))
如果他们都在包装一个
任务
,这将很好,但它会强制将
任务
对象无效地分配给real
ValueTask
,根据设计,否:

方法可能会返回此值类型的实例,因为它们的操作结果可能会同步可用,并且该方法的调用频率很高,以至于为每个调用分配新任务的成本很高

例如,考虑一种方法,它可以返回一个“代码>任务/<代码>,将缓存任务作为一个公共结果或<代码> 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
来减少分配,但无论如何都要借助分配来使用它。虽然知道它是设计出来的很好,但我看不出有任何理由。除了自己实现任务外,还不清楚什么是缓存任务的简单方法。Had
Task。WhenAny
Task。WhenAll
ValueTask
s有重载,事情会简单得多。在考虑了@marc gravell在该链接和其他链接中提到的注意事项后,这个答案@Nathan我开始在我的API中使用ValueTask。但是仍然没有类似于
任务的东西。Whalll
…@stuartd,同意-只是指出围绕主题的文档是。。。老化:)这不能满足调用AsTas的问题