21

所以我有以下代码

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 之前,主线程是阻塞的(我的意思是应用程序本身被冻结)。我的代码显然有问题,我不知道是什么。我以前从未使用过异步/等待。这是我的第一次尝试。

编辑:

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

4

4 回答 4

40

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

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

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

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

异步与多线程有关是一个常见的误解。可以,但在很多情况下并非如此。一个新线程不会仅仅因为方法是隐式产生的async;为了产生一个新线程,它必须在某个时候明确地完成。如果您特别希望该方法在不同的线程上运行,请使用Task.Run.

于 2013-11-06T13:52:09.207 回答
12

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

警告 1 此异步方法缺少“等待”运算符,将同步运行。考虑使用 '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");
}
于 2013-11-06T13:48:42.350 回答
8

您没有异步执行任何操作。尝试:

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

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

于 2013-11-06T13:46:53.707 回答
0

DoSomethingAsync Thread.Sleep之前调用await并且DoSomething2Async没有完成异步任务。

于 2013-11-06T13:49:57.430 回答