await关键字阻塞主线程

时间:2013-11-06 13:44:33

标签: c# async-await

所以我有以下代码

private async void button1_Click(object sender, EventArgs e)
{
    await DoSomethingAsync();

    MessageBox.Show("Test");
}

private async Task DoSomethingAsync()
{
    for (int i = 0; i < 1000000000; i++)
    {
        int a = 5;
    }; // simulate job

    MessageBox.Show("DoSomethingAsync is done");

    await DoSomething2Async();
}

private async Task DoSomething2Async()
{
    for (int i = 0; i < 1000000000; i++)
    {
        int a = 5;
    } // simulate job

    MessageBox.Show("DoSomething2Async is done");
}

在显示两个MessageBox之前,主线程是块(我的意思是应用程序本身被冻结)。我的代码显然有问题,我无法弄清楚是什么。我以前从未使用async / await。这是我的第一次尝试。

修改

实际上我想要做的是异步启动DoSomethingAsync的执行,这样当单击按钮时,即使DoSomethingAsync不完整,MessageBox.Show("Test");也会执行。

4 个答案:

答案 0 :(得分:32)

我认为你误解了异步意味着什么。 这并不意味着该方法在另一个线程中运行!!

异步方法同步运行直到第一个await,然后向调用者返回Task(除非它是async void,然后它不返回任何内容)。当正在等待的任务完成时,执行将在await之后继续执行,通常在同一个线程上(如果它有SynchronizationContext)。

在您的情况下,Thread.Sleep在第一个等待之前,因此它会在控制权返回给调用者之前同步执行。但即使它在await之后,它仍会阻止UI线程,除非您专门配置awaiter不捕获同步上下文(使用ConfigureAwait(false))。

Thread.Sleep是一种阻止方法。如果你想要一个异步等价物,请按照Sriram Sakthivel的回答中的建议使用await Task.Delay(3000)。它将立即返回,并在3秒后恢复,而不会阻止UI线程。

异步与多线程有关,这是一种常见的误解。它可以,但在许多情况下它不是。不会因为方法为async而隐式生成新线程;对于要生成的新线程,必须在某个时刻明确地完成。如果您特别希望该方法在其他线程上运行,请使用Task.Run

答案 1 :(得分:10)

您应该注意,标有async的方法在缺少await时将不再表现为异步。你有关于此的编译器警告。

  

警告1此异步方法缺少'await'运算符并将运行   同步。考虑使用'await'运算符等待   非阻塞API调用,或'await Task.Run(...)'来执行CPU绑定工作   在后台线程上。

你应该注意那些警告。

异步执行。当我说异步时,它实际上是异步而不是另一个线程中的同步作业。 使用真正异步的Task.Delay。使用Task.Run进行一些耗费CPU的工作。

private async void button1_Click(object sender, EventArgs e)
{
   await DoSomethingAsync();
}

private async Task DoSomethingAsync()
{
    await Task.Delay(3000); // simulate job

    MessageBox.Show("DoSomethingAsync is done");

    await DoSomething2Async();
}

private async Task DoSomething2Async()
{
    await Task.Delay(3000); // simulate job

    MessageBox.Show("DoSomething2Async is done");
}

答案 2 :(得分:5)

你没有异步做任何事情。尝试:

await Task.Run(() => Thread.Sleep(3000))

当你等待一个方法时,你实际上正在做的是提供一个回调,其后面的代码就像它完成后执行的那样(在以前的异步版本中,你实际上必须自己设置它)。如果你没有await任何东西,那么这个方法实际上不是异步方法。

有关详细信息,您可能会比在此处查看更糟糕:

http://msmvps.com/blogs/jon_skeet/archive/2011/05/08/eduasync-part-1-introduction.aspx

按照您的编辑;试试这个:

public void MyMessageBox()
{
  var thread = new Thread(() =>
  {
      MessageBox.Show(...);
  });
  thread.Start();
}

虽然,你确定它真的是你想要的消息框吗?我原以为状态栏/进度条更新会更合适。

答案 3 :(得分:0)

DoSomethingAsync之前调用Thread.Sleep awaitDoSomething2Async之前调用异步任务。