我应该在调用异步方法的方法中返回 ValueTask 或 Task

Axa*_*kat 9 c# task-parallel-library azure-cosmosdb

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

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

        return Result.Ok();
    }
    catch (Exception e)
    {
        return Handle(e); // returns a `Result`
    }
}
Run Code Online (Sandbox Code Playgroud)

_c 是ContainerCosmosDB 的。

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

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

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

class Result
{
    public static Result Ok() { return new Result(); }.
}
Run Code Online (Sandbox Code Playgroud)

因此,我有两种方法,一种是Task<Result> CreateUserAsync(User user),它返回一个简单的Task,而不等待其中的任何内容,但我最后调用的另一个任务return CreateAsync(userEntity);等待其中的内容。

因此,根据我的理解,CreateAsync()应该保留为Task返回类型,因为它在其中执行异步操作,但是我调用它的方法(即CreateUserAsync())也需要保留Task,还是可以成为ValueTask

Mar*_*ell 15

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

  1. 如果作业立即(同步)完成,则 aTask<T>每次都需要分配(除了一些关于布尔值和小整数的琐碎情况),其中 -as aValueTask<T>可以避免这种情况下的分配
  2. 在真正异步的情况下,ValueTask<T> 有可能摊销分配,尽管这需要特殊代码(IValueTaskSource<T>等)
  3. aTask<T>可以等待多次,而 aValueTask<T>只能等待一次(当等待多次时,行为是未定义的,作为上面“2”的副作用)

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

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

  • 这是有道理的,就我而言,因为我正在连接到数据库并插入一个项目,所以根据我的理解,这将被视为“异步”操作。因此,我应该坚持使用“Task”作为我的返回类型。 (2认同)