返回一个任务而不是等待内部方法调用

6

我看到一些同事的代码,他选择不等待数据库调用直接返回任务。例如:

public Task<UpdateResult> AddActivityAsync(ClaimsPrincipal principal, Activity activity)
{
    return _userManager.SaveToDatabaseAsync(principal, activity);
}

由于_userManager.SaveToDatabaseAsync是异步的,我会这样实现

public async Task<UpdateResult> AddActivityAsync(ClaimsPrincipal principal, 
                                                                      Activity activity)
{
    return await _userManager.SaveToDatabaseAsync(principal, activity);
}

这个方法的调用方法总是等待它:
await _profile.AddActivityAsync(..., ...)

不使用async方法,而只是返回Task并让调用者await它是否有任何好处?我认为我们必须一直写Async...

2个回答

4

这取决于具体情况。

例如,如果待等待的代码与处于using上下文中的对象相关联(或者手动释放),则必须等待:

using (SomeDisposableType obj = ...)
{
    await obj.SomeOperationAsync();
}

如果您在不等待任务的情况下返回它,那么该方法可能会抱怨对象在完成其工作之前就已被处理掉了。(并非所有可处理的对象在处理后尝试执行某些操作时都会抛出ObjectDisposedException,但通常建议这样做)。考虑相反的情况:

using (SomeDisposableType obj = ...)
{
    // This returns the Task that represents the async operation,
    // and because it returns, the control goes out of the using
    // scope, and therefore obj gets disposed.
    return obj.SomeOperationAsync();
}

因此,在某些情况下,等待是必要的。如果不是这种情况,我真的想不出为什么不能返回Task本身。


是的,我同意在某些情况下必须使用 await。但是对于不需要它的情况,只需返回内部结果,哪种做法更好呢? - Raphaël
在使用中,必须注意must await if inside using的重要警告。我曾经因为这个代码问题而被困扰了数月,却一直未能捕获到异常...理想情况下,编译器应该阻止在using块内返回任务,希望有一天能够实现!我发现这个问题与我的相关,链接如下:https://stackoverflow.com/questions/63584024/simple-injector-instance-is-requested-outside-the-context-of-an-active-async-s/63584565#63584565。我认为这个答案已经更新到Balazs,因为它涵盖了这个重要点。 - morleyc

1
如果方法带有 `async` 关键字,你不需要等待返回 `Task` 的方法,代码会自动异步运行。你的同事已经把这个关键字删掉了,故意让这些代码同步运行,并让调用代码实现它们自己的选择。取决于代码运行在哪个层级,如果是深层次的话,可能更好的方式是让更高层的代码选择异步执行并在那里维护控制。你绝对不需要“从头到尾都写异步”。`async` 关键字的目的只是标记一个方法能够返回 `Task` 并且能够使用 `await` 关键字。`await` 关键字是调用框架来执行任务并提供异步处理的方式。简而言之:这是一种选择。我欢迎任何人在此基础上改进或纠正我,因为我不是大师。

3
async/await 不是多线程 - http://blog.stephencleary.com/2013/11/there-is-no-thread.html - Lloyd
所以,如果它只是一个异步方法的包装器,没有必要标记包装器为异步,只需返回任务即可。如果我将其标记为异步,每次等待时是否会增加开销?在这种情况下,对于简单的包装器,不等待似乎更好。 - Raphaël
1
@lloyd 在我看来,那篇文章并没有清楚地说明在线程之外发生了什么。它似乎是在说使用了一个线程,但不一定是新的线程。所以,await 是导致新上下文的原因? - Matt W

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接