【问题标题】:Async/Await: Unexpected behaviour of ConfigureAwait异步/等待:ConfigureAwait 的意外行为
【发布时间】:2013-09-29 00:10:48
【问题描述】:

如果您在 ASP.NET MVC 中执行以下代码,您可以在调试窗口中看到它会在 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")。但我希望使用这两种方法,结果必须相同。完全不清楚,为什么会这样。

【问题讨论】:

  • ConfigureAwait(true) 是做什么的? bool 参数控制是否“在原始上下文中编组”——这不是你想要的吗?
  • 我预计如果我将ConfigureAwait(false) 添加到await 它将影响整个异步调用链,而不仅仅是当前方法,因为否则使用它几乎没有性能优势。

标签: c# asp.net asp.net-mvc task-parallel-library async-await


【解决方案1】:

如果您使用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);
}

【讨论】:

    【解决方案2】:

    您可以创建自己的等待者,以使用await 延续回调使文化流动,即使它发生在不同的池线程上也是如此。所以,你的电话看起来像:

    await SomeMethod().WithCulture();
    

    Stephen Toub 在PFX Team blog 上准确展示了如何执行此操作,请查找CultureAwaiter

    【讨论】:

      【解决方案3】:

      (来自手机)

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

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

      ConfigureAwait(false) 指示生成的状态机不要发布到捕获的(如果有)SynchronizationContext。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-10-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-12-16
        相关资源
        最近更新 更多