界面:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace taskTest { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { string str; public MainWindow() { InitializeComponent(); } public async Task<int> Method1() { int count = 0; await Task.Run(() => { for (int i = 0; i < 500; i++) { str += "A"; count += 1; Thread.Sleep(TimeSpan.FromSeconds(0.01)); } } ); return count; } public void Method2() { for (int i = 0; i < 200; i++) { str += "B"; } } private void btn1_Click(object sender, RoutedEventArgs e) { str = ""; txt_boxMessage.Text = ""; txt_boxMessage.Text = "Method1 begin:" + DateTime.Now + "\r\n"; Method1(); txt_boxMessage.Text += "Method1 end:" + DateTime.Now + "\r\n"; txt_boxMessage.Text += "Method1 end:" + DateTime.Now + "\r\n"; Method2(); txt_boxMessage.Text += "Method1 end:" + DateTime.Now + "\r\n"; this.txt_box1.Text = str; int numA = Regex.Matches(str, "A").Count; int numB = Regex.Matches(str, "B").Count; MessageBox.Show("A:" + numA, "B:" + numB); } private void btn2_Click(object sender, RoutedEventArgs e) { MessageBox.Show(str); } } }
如查按上面的代码写一段程序,语法检查会有一小段提示:
“由于此调用不会等待,因此在此调用完成之前将会继续执行当前方法。请考虑将 "await" 运算符应用于调用结果。”
运行程序,点按钮1,结果:
可以看到,由于Method1异步执行,只有其中一部分数据返回。注意这时程序的界面没有阻塞,这就是异步编程带来的好处。
之所以A只返回了一个字符,是由于执行的时间不够,程序还在后台继续生成字符串。
我们再点按钮2,这时可以看到生成的完整字符串:
异步调用虽然不阻塞主线程,但是程序运行的结果并不是我们想要的,所以更改代码如下:
private async Task btn1_ClickAsync() { str = ""; txt_boxMessage.Text = ""; txt_boxMessage.Text = "Method1 begin:" + DateTime.Now + "\r\n"; await Method1(); txt_boxMessage.Text += "Method1 end:" + DateTime.Now + "\r\n"; txt_boxMessage.Text += "Method1 end:" + DateTime.Now + "\r\n"; Method2(); txt_boxMessage.Text += "Method1 end:" + DateTime.Now + "\r\n"; this.txt_box1.Text = str; int numA = Regex.Matches(str, "A").Count; int numB = Regex.Matches(str, "B").Count; MessageBox.Show("A:" + numA, "B:" + numB); } private void btn1_Click(object sender, RoutedEventArgs e) { btn1_ClickAsync(); }
这时再点击按钮1:
程序开始运行结果如下,注意此时UI线程仍然未被阻塞,用户仍然可以进行界面操作:
大约过了5秒
所以await async的意思就是:等待异步执行
好处是在这个等待过程中,主界面的UI线程没有被阻塞,还可以进行其他操作。
继续更改代码,将Method2也改为异步执行
public async Task<int> Method2() { int count = 0; await Task.Run(() => { for (int i = 0; i < 500; i++) { str += "B"; count += 1; Thread.Sleep(TimeSpan.FromSeconds(0.01)); } } ); return count; }
这时的结果如下:
可以看到click事件完成后,Method2还没有运行完毕,我们点击按钮2,可以看到完整的运行结果:
我们在调用 Method2时也加上关键字await,这时代码如下:
private async Task btn1_ClickAsync() { str = ""; txt_boxMessage.Text = ""; txt_boxMessage.Text = "Method1 begin:" + DateTime.Now + "\r\n"; await Method1(); txt_boxMessage.Text += "Method1 end:" + DateTime.Now + "\r\n"; txt_boxMessage.Text += "Method2 begin:" + DateTime.Now + "\r\n"; await Method2(); txt_boxMessage.Text += "Method2 end:" + DateTime.Now + "\r\n"; this.txt_box1.Text = str; int numA = Regex.Matches(str, "A").Count; int numB = Regex.Matches(str, "B").Count; MessageBox.Show("A:" + numA, "B:" + numB); }
运行程序,点击按钮1,大约10秒后,可以看到运行结果:
总结:
1、不使用await关键字,在异步线程中,由于可能没有足够的时间等待返回结果,所以可能不会在预期的代码段上取得正确的结果。
2、Method1使用await关键字异步执行,Method2为不涉及异步执行的方法,会在Method2处阻塞主线程。
3、都使用await关键字,在异步线程中,异步方法是按队列执行的,结果与同步方法一样。好处是没有阻塞主线程执行其它任务。
所以在asp.net core中的代码:
await TryUpdateModelAsync
await _context.SaveChangesAsync()
是顺序执行的,后面的代码无论怎么写,都必然是顺序执行。
但有一点除外,那就是:同步调用异步方法,也就是异步方法前不加await关键字进行调用,这时程序的运行结果可能会变得非常诡异。