在单个方法中有效地混合使用同步和异步方法?


11

好的,听起来很奇怪,但是代码非常简单,可以很好地说明情况。

public virtual async Task RemoveFromRoleAsync(AzureTableUser user, string role)
{
    AssertNotDisposed();
    var roles = await GetRolesForUser(user);
    roles.Roles = RemoveRoles(roles.Roles, role);
    await Run(TableOperation.Replace(roles));
}

(我知道我在下面的摘要中说的有点像,但以上是实际生产代码中的一种实际方法,该代码实际上正在执行我在这里要问的事情,并且我实际上对您的实际审查很感兴趣相对于异步/等待模式的正确性。)

我现在使用async/ awaitmore越来越多地遇到这种模式。该模式由以下事件链组成:

  1. 等待初始呼叫,这会给我一些我需要处理的信息
  2. 同步处理该信息
  3. 等待最后的通话,以保存更新的工作

上面的代码块通常是我如何处理这些方法。我await是第一个电话,我必须这样做,因为它是异步的。接下来,我要做的工作是不受IO或资源限制的,因此不是异步的。最后,我保存了我的工作,这也是一个async电话,而不再是我的工作await

但这是处理这种模式的最有效/正确的方法吗?在我看来,我可以跳过await上一个电话,但是如果失败了怎么办?我是否应该使用Task诸如ContinueWith将同步工作与原始调用链接在一起的方法?我现在只是不确定我是否正确处理了这一点。

给定示例中的代码,是否有更好的方法来处理此async / sync / async方法调用链?


在我看来,这段代码尽可能简短易懂。我能想到的任何东西都会带来不必要的复杂性。
欣快2014年

@Euphoric:我是否应该等待最后一个电话?如果我不加投掷怎么办?会和现在有什么不同吗?
2014年

Answers:


3

是的,我认为这是正确的方法。

您不能跳过第二个await。如果您这样做了,那么该方法似乎完成得太早了(在实际完成删除之前),并且您将永远无法确定删除是否失败。

ContinueWith()在这里看不到会有什么帮助。您可以使用它来避免使用await,但是这会使您的代码更复杂且可读性更差。这就是整个要点await:与使用延续相比,使编写异步代码更简单。


0

处理此模式的方法是确保所有I / O都是异步的。同步I / O方法导致当前线程在等待来自I / O目标(网络,文件系统等)的响应时阻塞。

需要考虑的另一件事是,await当您需要返回值或需要等待代码完成其他操作之前,应使用此方法。如果您不需要这些东西,可以使用来“触发并忘记”您的异步方法Task.Run

因此,为了最有效地利用计算资源,RemoveRoles应该执行任何I / O操作,await RemoveRolesAsync并且调用的I / O方法RemoveRolesAsync也应该是异步的(并且可能正在等待)。

如果性能不是您最关心的问题,那么可以在异步线程上执行一些同步I / O。虽然这是技术债务。(在这种情况下,您可能要使用调用第一个异步方法ConfigureAwait,具体取决于代码的运行位置。)

这是对最佳实践的更深入研究-https: //msdn.microsoft.com/zh-cn/magazine/jj991977.aspx

以下是有关ConfigureAwait在不同环境中,如ASP.NET,的WebAPI等行为的一些注意事项- /programming/13489065/best-practice-to-call-configureawait-for-all-server-side -码


2
您永远都不应使用Task.Run触发并忘记代码。应该等待所有任务。如果您不等待任务,并且任务在“未观察到”异常的状态下被垃圾回收,则会导致在运行时引发UnobservedTaskException并可能导致应用程序崩溃,具体取决于框架版本。
Triynko
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.