C# 使用async Wait时语句执行的随机序列

C# 使用async Wait时语句执行的随机序列,c#,asynchronous,async-await,C#,Asynchronous,Async Await,我有一个非常简单的程序,如下所示 public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); Task task = DemoCompletedAsync(); MessageBox.Show("Method returned");

我有一个非常简单的程序,如下所示

 public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            Task task = DemoCompletedAsync();

            MessageBox.Show("Method returned");            
            MessageBox.Show("Exiting .....");
        }

        static async Task<string> DemoCompletedAsync()
        {
            MessageBox.Show("Before first await");

            await Task.Run(() => GetID());

            MessageBox.Show("After first await");

            return "Demo";
        }

        public static int GetID()
        {
            Thread.Sleep(2000);
            return 1;
        }
    }
公共部分类主窗口:窗口
{
公共主窗口()
{
初始化组件();
任务任务=DemoCompletedAsync();
Show(“返回的方法”);
MessageBox.Show(“正在退出…”);
}
静态异步任务DemoCompletedAsync()
{
MessageBox.Show(“在第一次等待之前”);
等待任务。运行(()=>GetID());
MessageBox.Show(“首次等待后”);
返回“演示”;
}
公共静态int GetID()
{
《睡眠》(2000年);
返回1;
}
}
在没有调试模式的情况下运行程序时,消息框以正确的顺序显示。 i、 e.
“在第一次等待之前”
“返回的方法”
“正在退出…”
“第一次等待之后”

但在调试时,语句会以随机方式执行,在
MainWindow()
DemoCompletedAsync

框中的消息以以下方式显示:
“在第一次等待之前”--来自
DemoCompletedAsync

“返回的方法”--来自
mainwWindows

“首次等待后”--来自
DemoCompletedAsync

“正在退出…”——从
main窗口


根据理论,在方法名称中使用
async
关键字告诉编译器,此方法将使用wait关键字等待异步操作,一旦遇到wait关键字,控件将传递给调用代码。但是,由await关键字表示的异步操作将继续执行。异步操作完成后,将执行方法的其余部分。所以为什么我会出现这种奇怪的行为,比如一些语句从
main窗口执行,而另一些语句则从
DemoCompletedAsync
相互切换。我还发现了另一个与消息框相关的问题。显示是模态对话框。第二个窗口在第一个窗口关闭之前不应出现。但在给定程序的情况下,当控件在
main window
DemoCompletedAsync

之间切换时,我实际上同时得到两个消息框。所有这些都是由于这一行:

Task task = DemoCompletedAsync();
您没有等待此任务,所以当指令指针到达实际线程时(此处为
task.Run(()=>GetID());
),下一行立即执行。所以执行的下一行可以是
MessageBox.Show(“返回的方法”)
MessageBox.Show(“首次等待后”)随机

但这里的要点是,你不能等待这个任务,因为你在构造函数中,它不能是异步的。此外,您不能像
DemoCompletedAsync().wait()
那样等待此任务,因为您处于
同步上下文中,将发生死锁

所以你应该这样称呼它:

DemoCompletedAsync().ConfigureAwait(false).GetAwaiter().GetResult();

并在GUI操作的
DemoCompletedAsync
方法中使用
Dispatcher.Invoke

消息框“Task completed”(任务已完成)显示在刚刚创建但尚未等待的位置。因此,这一信息具有欺骗性。我建议在消息中包含
任务.Status
。您实际上没有在等待任务,因为您的主方法没有等待它,因此消息将立即显示,您实际上在尝试做什么?“这只是一个任务的实验吗?”夏利是的,我正在试验任务。是,消息将立即显示。但它们应按顺序显示,即来自
main窗口的消息或来自
DemoCompletedAysnc的消息。为什么从
MainWindow
显示一些消息,然后从
DemoCompletedAsync
显示一些消息,然后再从
MainWindow
显示一些消息。来自
主窗口的消息应按顺序显示。应避免使用GetResult。ConfigureAwait很少是必需的。当您不想返回UI线程时,应该使用
ConfigureAwait(false)
。例如,它应该用在存储库或与用户界面无关的东西中。@HenkHolterman:你说得对,Henk。我们应该尽可能避免阻塞线程,但我试图解释senarios,并根据它当前的结构回答这个问题。另外,始终建议使用
ConfigureAwait(false)
来防止死锁+1您的回答中提到重组为
Form\u Load
event@Michael:请参考我的上述评论。另请看我答案中的最后一行,这是您提到的解决方案UI@Arman,我的评论是@henkholtman comment。但是
ConfigureAwait(false)
并不是为了防止死锁。我同意它可以做到这一点,但它不应该到处都是。您需要知道它的功能,因此需要了解
上下文<代码>配置等待
仅适用于实际执行
等待
的情况。GetWaiter'不算数。如果您在一个UI线程上,并且有一个
await
语句,您通常会在
await
之后返回UI线程。如果您等待的任务具有
ConfigureAwait(false)
,它将返回另一个上下文,可能是一个新线程。