【问题标题】:Async-Await Not Executing On Expected Thread异步等待未在预期线程上执行
【发布时间】:2016-05-22 14:38:41
【问题描述】:

为了理解 async/await,我制作了一个带有一个按钮的小示例 WPF 应用程序。当点击它会做一些“工作”:

private async void goButtonClicked(object sender, EventArgs e)
        {

            WhatThreadAmI();
            var task = populateRawData().ConfigureAwait(false);
            WhatThreadAmI();
            BusyIndicator.IsBusy = true;    

            await task;
            WhatThreadAmI(); //this isnt on the main thread - why??
            BusyIndicator.IsBusy = false;

            Console.WriteLine("fin");
        }

“WhatThreadAmI”只是将当前线程与我在初始化时保存的 UI 线程进行比较。

public bool IsMainThread => uiThread == Thread.CurrentThread;

我希望它的输出是 True - True - True,在填充原始数据方法中调用“WhatThreadAmI”会返回 false。

实际发生的是真 - 真 - 假,填充原始数据方法中的“WhatThreadAmI”调用返回真。

我知道我必须在这里遗漏一些非常基本的东西,但是有人可以帮助我了解发生了什么吗?

【问题讨论】:

  • 即使是这样,您在创建任务时确实会说.ConfigureAwait(false),这意味着“我不需要在原始上下文中恢复。”只要您await 执行该任务,您的上下文就会发生变化,所以这并不奇怪。当您只是创建任务但尚未等待它时,上下文不会改变,因此前两个WhatThreadAmI 返回true
  • 有道理 - 谢谢!

标签: c# async-await


【解决方案1】:
var task = populateRawData().ConfigureAwait(false);

ConfigureAwait(false) 返回一个配置的任务等待器,不会在捕获的上下文中恢复。我在我的博客上详细解释了how await captures and resumes on contextConfigureAwait(false) 的使用意味着在上下文中捕获和恢复。在这种情况下,“上下文”是 UI 线程。因此,await 不会恢复 UI 线程,因为ConfigureAwait(false) 明确告诉await 它不需要。

附带说明,该代码中的task 变量包含Task。将ConfigureAwait 的结果放在变量中而不是await 是非常不寻常的。下面的例子是等价的,而且——我认为——更清楚地表达了正在发生的事情:

WhatThreadAmI();
var task = populateRawData();
WhatThreadAmI();
BusyIndicator.IsBusy = true;    

await task.ConfigureAwait(false);
WhatThreadAmI(); //this isnt on the main thread - why??
BusyIndicator.IsBusy = false;

换一种说法:它是ConfigureAwait,而不是ConfigureTask。它根本不会改变任务; ConfigureAwaitawait一起使用才有意义。

【讨论】:

    【解决方案2】:

    Task 对象是对 .Net 线程池的抽象。一个线程可能开始执行代码,等待未完成的任务,然后从另一个线程恢复。

    通过这种方式,您可以在不阻塞的情况下最大限度地利用您的 CPU。线程对任务没有意义。等待完成后,可用线程将继续执行您的任务。它可能是过去执行任务的线程,也可能不是。

    【讨论】:

    • 我喜欢这个答案,因为它是问题中隐含的关键细节的核心。如果您依赖特定线程的某些行为,那么 Async Await 不是您的解决方案。我会将 Async Await 描述为“如果可以,请尝试并行化”。 “随机”使用线程。如果您想理解它,这里有一个设计和架构细节并非易事。如果您想理解,原始问题是一个很好的问题。为什么它不会在所有环境中都像预期的那样是原始问题浮出水面的一个很好的例子。
    【解决方案3】:

    更改配置等待(假);配置等待(真);或者直接删除它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-09
      • 2011-12-15
      • 2019-04-19
      • 1970-01-01
      • 2020-03-04
      相关资源
      最近更新 更多