为什么我的异步代码不能运行async?

时间:2016-05-20 16:18:10

标签: c# asynchronous async-await

警告我是async / await的新手,所以我可能完全误解了这一点!

我试图弄清楚这些东西是如何工作的,并在WPF窗口的视图中尝试了一些简单的代码。我添加了一个按钮单击事件处理程序,并添加了一些同步和异步方法,如下所示......

public partial class MainWindow {
  private Random _r = new Random(DateTime.Now.Millisecond);

  public MainWindow() {
    InitializeComponent();
  }

  private async void Bleah_Click(object sender, RoutedEventArgs e) {
    LstMessages.Items.Clear();
    AddToMsg("Starting...");
    DoSyncStuff();
    await DoStuffAsync();
    DoMoreStuffSync();
    AddToMsg("Done");
  }

  private void DoSyncStuff() {
    int delay = _r.Next(500, 1500);
    AddToMsg("DoSyncStuff - waiting for " + delay + "ms");
    Thread.Sleep(delay);
    AddToMsg("DoSyncStuff - finished");
  }

  private void DoMoreStuffSync() {
    int delay = _r.Next(500, 1500);
    AddToMsg("DoMoreStuffSync - waiting for " + delay + "ms");
    Thread.Sleep(delay);
    AddToMsg("DoMoreStuffSync - finished");
  }

  private async Task DoStuffAsync() {
    await Task.Run(() => {
      int delay = _r.Next(500, 1500);
      AddToMsg("DoStuffAsync - waiting for " + delay + "ms");
      Thread.Sleep(delay);
      AddToMsg("DoStuffAsync - finished");
    });
  }

  private void AddToMsg(string msg) {
    Dispatcher.BeginInvoke(
      new Action(() => { LstMessages.Items.Add(DateTime.Now.ToString("HH:mm:ss.fff") + " - " + msg); }));
  }

LstMessages是窗口上的ListBox。

当我点击按钮时,我看到这三种方法总是按照我调用的顺序执行,而不管每次延迟的长度。

我显然误解了这些东西是如何工作的,但是在阅读了几个小时之后,尝试了很多代码的变化,我无法按照我的预期工作。

任何人都可以澄清我在这里做错了什么吗?

4 个答案:

答案 0 :(得分:2)

要做的就是在代码中删除await关键字。

引用Eric Lippert的blog post

  

每当“等待”任务时,当前方法的其余部分将作为任务的继续进行注册,然后控制立即返回给调用者。任务完成后,将调用continuation并启动方法。

通过添加await关键字,您有效地说"一旦此异步方法完成,继续使用此方法的其余部分"。

使用返回值的方法可能更容易理解这一点。以下程序将立即启动两个方法,并在调用sync方法后await async方法的结果。您可以尝试移动await线来观察行为的差异。

class Program
{
    static void Main(string[] args)
    {
        MainAsync();
        Console.ReadKey();
    }

    static async void MainAsync()
    {
        var task = GetNumberAsync();
        var syncNumber = GetNumber();
        var asyncNumber = await task; // moving this line above "GetNumber();" will make these run in order

        Console.WriteLine(syncNumber);
        Console.WriteLine(asyncNumber);
    }

    private static int GetNumber()
    {
        Console.WriteLine("DoSomeWork - started");
        Thread.Sleep(1000);
        Console.WriteLine("DoSomeWork - finished");
        return 11;
    }

    private static async Task<int> GetNumberAsync()
    {
        Console.WriteLine("GetNumberAsync - started");
        await Task.Delay(1000);
        Console.WriteLine("GetNumberAsync - finished");
        return 22;

    }
}

答案 1 :(得分:1)

要理解的重要一点是async and await keywords don't cause additional threads to be created.Task.Run()可以将工作转移到另一个线程)。那你的代码到底发生了什么?

因此,在您的代码中,第一次调用DoSyncStuff()会暂停主线程。在DoStuffAsync()完全完成之后,您的DoSyncStuff()电话才会被执行。

您对DoStuffAsync的调用被触发,好像它是异步的 - 但由于您在调用函数中使用了await关键字'await DoStuffAsync()',主线程控制将返回Bleah_Click()调用者(这对你的目的不会做任何超级有趣的事情)。 DoStuffAsync()完成后,控件将返回Bleah_Click,并执行DoMoreStuffSync() - 这会再次暂停主线程。

关于你的问题:我不能告诉你你做错了什么,因为你没有真正指定你想要的结果 - 如果你想暂停你的UI线程执行并执行列出的所有功能顺序,然后你已经做好了一切。

答案 2 :(得分:1)

尝试这种方法,看起来你正在开始一个异步方法,但会立即在UI线程中等待它。

private async void Bleah_Click(object sender, RoutedEventArgs e) 
{
    LstMessages.Items.Clear();
    AddToMsg("Starting...");
    DoSyncStuff();
    Task t = DoStuffAsync();
    DoMoreStuffSync();
    await t;
    AddToMsg("Done");
}

答案 3 :(得分:0)

当您在主UI线程上执行所有操作时,您所看到的内容是有道理的。您需要创建和管理自己的Thread / BackgroundWorker对象,或者向ThreadPool提交方法

BackgroundWorker

Thread

ThreadPool

每种方法都有自己的优点和缺点,可以在其他答案中找到。阅读这些链接并试用示例