Async/Await:ConfigureAwait 的意外行为

本文关键字:意外 ConfigureAwait Await Async | 更新日期: 2023-09-27 18:32:26

如果在 MVC ASP.NET 中执行以下代码,则可以在调试窗口中看到它将在 await 后正确还原线程的区域性,即使 ManagedThreadId 发生更改也是如此:

public async Task<ActionResult> Index()
{
    Thread.CurrentThread.CurrentUICulture = new CultureInfo("de-DE");
    Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
    Debug.WriteLine(Thread.CurrentThread.CurrentUICulture);
    await SomeMethod();
    Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
    Debug.WriteLine(Thread.CurrentThread.CurrentUICulture);
    return View();
}
private async Task SomeMethod()
{
    await Task.Delay(100).ConfigureAwait(false);
}

然后我只是将 ConfigureAwait(false) 从 SomeMethod() 移动到 Index(),除此之外,它与上面的代码相同:

public async Task<ActionResult> Index()
{
    Thread.CurrentThread.CurrentUICulture = new CultureInfo("de-DE");
    Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
    Debug.WriteLine(Thread.CurrentThread.CurrentUICulture);
    await SomeMethod().ConfigureAwait(false);
    Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
    Debug.WriteLine(Thread.CurrentThread.CurrentUICulture);
    return View();
}
private async Task SomeMethod()
{
    await Task.Delay(100);
}

现在它不会恢复我的文化,但总是将其设置为 new CultureInfo("en-US") .但我希望使用这两种方法,结果必须相同。绝对不清楚为什么会发生这种情况。

Async/Await:ConfigureAwait 的意外行为

如果您使用 await task.ConfigureAwait(false) ,那么该方法的其余部分(以及您从那里调用的任何内容)将不会在原始上下文中执行。但这不会影响逻辑调用树中更高位置的任何代码。

我认为这是唯一合乎逻辑的方法。如果更高的代码必须在原始上下文中执行(这很常见),那么库代码深处的ConfigureAwait()真的不应该影响它。

为了使这一点更具体,如果按照您的行为ConfigureAwait()则在 WinForms 中使用 await 的以下简单示例将不起作用:

async void ButtonClicked(object sender, EventArgs e)
{
    textBlock.Text = "Downloading";
    await DownloadAsync();
    textBlock.Text = "Finished";
}
async Task DownloadAsync()
{
    data = await new HttpClient().GetStringAsync(url).ConfigureAwait(false);
}

您可以创建自己的等待者,以使用await延续回调使区域性流,即使它发生在不同的池线程上也是如此。因此,您的呼叫将如下所示:

await SomeMethod().WithCulture();

Stephen Toub在PFX团队博客上展示了如何做到这一点,寻找CultureAwaiter

(来自手机)

您不仅会失去线程文化。你正在失去整个上下文。

当存在同步上下文时,延续将发布到该同步上下文。在 ASP.NET 中,这是一个请求处理程序线程,在客户端 UI 中,这是一个 UI 线程。

ConfigureAwait(false) 指示生成的状态机不要发布到捕获的(如果有)同步上下文。

索引永远不应该使用它,但从那里调用的任何代码都应该使用它。