界面:
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關鍵字進行調用,這時程序的運行結果可能會變得非常詭異。