Async / Await:ConfigureAwait的意外行为

时间:2013-09-23 23:12:08

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

如果在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")。但我希望使用这两种方法,结果必须相同。目前还不清楚,为什么会发生这种情况。

3 个答案:

答案 0 :(得分:6)

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

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

为了使其更加具体,如果await根据您的行为表现出来,以下在Winforms中使用ConfigureAwait()的简单示例将无效:

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);
}

答案 1 :(得分:1)

您可以创建自己的awaiter,使文化流程具有await延续回调,即使它发生在不同的池线程上也是如此。所以,你的电话会是这样的:

await SomeMethod().WithCulture();

Stephen Toub在PFX Team blog上详细说明了如何执行此操作,查找CultureAwaiter

答案 2 :(得分:0)

(来自手机)

你不仅失去了线程文化。你正在失去整个背景。

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

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

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