警告我是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。
当我点击按钮时,我看到这三种方法总是按照我调用的顺序执行,而不管每次延迟的长度。
我显然误解了这些东西是如何工作的,但是在阅读了几个小时之后,尝试了很多代码的变化,我无法按照我的预期工作。
任何人都可以澄清我在这里做错了什么吗?
答案 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提交方法
每种方法都有自己的优点和缺点,可以在其他答案中找到。阅读这些链接并试用示例