【发布时间】:2015-01-22 10:16:01
【问题描述】:
我目前正在编写大量 async 库代码,并且我知道在每次异步调用之后添加 ConfigureAwait(false) 以避免将延续代码编组回原始代码(通常是 UI)的做法线程上下文。由于我不喜欢未标记的布尔参数,因此我倾向于将其写为 ConfigureAwait(continueOnCapturedContext: false)。
我添加了一个扩展方法以使其更具可读性(并在一定程度上减少打字):
public static class TaskExtensions
{
public static ConfiguredTaskAwaitable<TResult> WithoutCapturingContext<TResult>(this Task<TResult> task)
{
return task.ConfigureAwait(continueOnCapturedContext: false);
}
public static ConfiguredTaskAwaitable WithoutCapturingContext(this Task task)
{
return task.ConfigureAwait(continueOnCapturedContext: false);
}
}
所以现在我可以用await SomethingAsync().WithoutCapturingContext() 代替await SomethingAsync().ConfigureAwait(continueOnCapturedContext: false)。我认为这是一种改进,但是当我必须在同一个代码块中调用多个 async 方法时,即使这也开始令人讨厌,因为我最终会得到类似这样的东西:
await FooAsync().WithoutCapturingContext();
var bar = await BarAsync().WithoutCapturingContext();
await MoreFooAsync().WithoutCapturingContext();
var moreBar = await MoreBarAsync().WithoutCapturingContext();
// etc, etc
在我看来,它开始使代码的可读性降低很多。
我的问题基本上是这样的:有没有办法进一步减少这种情况(除了缩短扩展方法的名称)?
【问题讨论】:
-
你总是给你的所有参数加上标签吗?
-
您可以将
ConfigureAwait()移至较低级别。例如。在BarAsync()周围创建一个包装器,基本上是return await BarAsync().WithoutCapturingContext()。 -
你不只需要第一个
.WithoutCapturingContext()吗?第一次调用就足以“摆脱”原始上下文;后续延续是否在相同的非原始上下文中运行并不重要......对吗? (我的意思是,第一次调用可能会回调线程池中方法的其余部分,并且所有进一步的回调也可以在线程池中运行,因此您可以让等待者捕获线程池上下文。) -
@stakx - 没有。不能保证方法会异步返回(即
return Task.FromResult(42);对于潜在的异步方法来说是完全有效的返回)。所以如果你决定走那条路,你必须在每次通话中指定ConfigureAwait(false)。 -
@stakx 那么您将在执行时添加大量要完成的工作,这样您就可以减少作为程序员的打字工作。这意味着您的所有异步方法都需要等待线程池线程能够执行代码,然后才能开始处理任何其他工作,并且您正在添加一个额外的延续结束也是如此。当您开始对几乎所有异步方法以及打算在许多不同上下文(可能对性能敏感)中使用的库代码执行此操作时,就会出现问题。
标签: c# async-await code-formatting