我应该 return 调用异步方法的方法中的 ValueTask 或 Task

Should I return a ValueTask or Task in a method which calls an async method

我有一个具有以下签名的方法:

public async Task<Result> CreateAsync(T entity)
{
    try
    {
        await _c.CreateItemAsync(entity);

        return Result.Ok();
    }
    catch (Exception e)
    {
        return Handle(e); // returns a `Result`
    }
}

_c 是 CosmosDB 的 Container

现在,我有另一个这样的方法:

public Task<Result> CreateUserAsync(User user)
{
    var userEntity = new UserEntity { Id = user.Id, Name = user.Name };
    return CreateAsync(userEntity);
}

Result.Ok()是一个非常简单的自定义静态方法 class Result:

class Result
{
    public static Result Ok() { return new Result(); }.
}

所以,我有两种方法,一种是 Task<Result> CreateUserAsync(User user),return 是一个简单的 Task,无需等待其中的任何内容,但我最后调用的另一个任务return CreateAsync(userEntity); 等待里面的东西。

所以据我了解,CreateAsync() 应该保留为 Task return 类型,因为它在其中执行异步操作,但我调用它的方法(即CreateUserAsync()) 也需要保持 Task,或者可以变成 ValueTask?

它们在功能上相似,但有一些重要的区别:

  1. 如果作业立即(同步)完成,则每次都需要分配 Task<T>(布尔值和小整数周围的一些微不足道的情况除外),其中 ValueTask<T> 可以避免这种情况下的分配
  2. 在真正异步的情况下,ValueTask<T> 有潜力 摊销分配,尽管这需要特殊代码(IValueTaskSource<T> 等)
  3. a Task<T> 可以等待多次,而 ValueTask<T> 只应等待一次(当多次等待时,行为未定义,作为“2”的副作用"以上)

如果这是一个高吞吐量的代码路径,可能 useful/necessary 考虑机制的分配开销,在这种情况下 ValueTask<T> 开始变得非常诱人 - 但是,如果预先存在的代码可能已经 await 结果不止一次(违反第 3 个要点),这可能会有问题。第一个和第二个要点可能会产生重大影响 如果 异步机制是一个很大的开销(在您的场景中进行测量之后)。

如果这是一个低吞吐量的代码路径,老实说:做你想做的事。 Task<T> 的优点是不需要考虑第三个要点,这很吸引人。