为什么 ConfigureAwait (false)不是默认选项?

正如您所知,当您在代码中等待一个不需要捕获同步上下文的任务时,调用 Task.ConfigureAwait(false)是一个好主意,因为它可以在其他情况下调用 造成僵局

您需要多久捕获一次同步上下文?我的工作,很少。在大多数情况下,我使用的是“库”代码,这几乎迫使我一直使用 Task.ConfigureAwait(false)

所以我的问题很简单: 为什么 Task.ConfigureAwait(false)不是任务的默认选项?强制“高级”代码使用 Task.ConfigureAwait(true)不是更好吗?是有历史原因,还是我遗漏了什么?

21279 次浏览

仅仅因为 你的典型用例需要 ConfigureAwait (false) ,并不意味着它是“正确的”或最常用的选项。

设计异步/等待的目的之一是编写响应式 GUI 程序。在这种情况下,在将一些工作卸载到 Task 之后返回到 UI 线程是至关重要的,因为 UI 更新只能从大多数 Windows GUI 平台上的主线程发生。异步/等待帮助 GUI 开发人员做正确的事情。

这不是默认选项更有意义的唯一例子。我只能猜测,但是我怀疑对于 ConfigureAwait 默认值的决定是基于确保异步工作时尽可能少的摩擦,因为微软预计它将被用于最多的用例。并非所有人都编写框架。

看看链接中的第二个示例解决方案:

public async void Button1_Click(...)
{
var json = await GetJsonAsync(...);
textBox1.Text = json;
}


public class MyController : ApiController
{
public async Task<string> Get()
{
var json = await GetJsonAsync(...);
return json.ToString();
}
}

如果默认行为是 ConfigureAwait(false),则 textBox1.Text = json;语句将在随机线程池线程上执行,而不是在 UI 线程上执行。

这两个代码段看起来都像是某人可以合理编写的代码,默认情况下,其中一个代码段必须被破坏。由于死锁比线程不安全的访问危险性小得多,也更容易检测,因此选择 ConfigureAwait(true)作为默认值是更为保守的选择。

大多数与 .ConfigureAwait(false)一起工作的代码也可以与 .ConfigureAwait(true)一起工作,尽管是次优的。是的,不是所有的代码,但仍然是大多数。当前的默认设置允许最高比例的代码工作,而不需要修补一般程序员可能不理解的设置。

一个不同的默认设置只会导致成千上万的问题,为什么代码不工作,更糟糕的是,成千上万的答案形式是“微软糟透了,他们让你在每个程序中编写 Control.CheckForIllegalCrossThreadCalls = false;。为什么这不是默认情况?”而不是实际添加适当的 .ConfigureAwait(true)调用。