網站首頁 編程語言 正文
一、引言
我們先來看下面的一個小示例:一個Winfrom程序,界面上有一個按鈕,有兩個異步方法,點擊按鈕調用兩個異步方法,彈出執行順序,代碼如下:
using System; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace TPLDemoSln { public partial class Form1 : Form { public Form1() { InitializeComponent(); } ////// 按鈕點擊事件 /// /// /// private async void btnStart_Click(object sender, EventArgs e) { string i1 = await F1Async(); MessageBox.Show("i1=" + i1); string i2 = await F2Async(); MessageBox.Show("i2=" + i2); } ////// 異步方法F1 /// ///private Task F1Async() { MessageBox.Show("F1 Start"); return Task.Run (() => { // 休眠1秒 Thread.Sleep(1000); MessageBox.Show("F1 Run"); return "F1"; }); } /// /// 異步方法F2 /// ///private Task F2Async() { MessageBox.Show("F2 Start"); return Task.Run (() => { // 休眠2秒 Thread.Sleep(2000); MessageBox.Show("F2 Run"); return "F2"; }); } } }
在上面的代碼中,Task.Run()是用來把一個代碼段包裝為Task
運行程序,可以得到如下的輸出順序:
F1 Start->F1 Run->i1=F1->F2 Start->F2 Run->i2=F2。
我們對按鈕事件進行修改,修改為下面的代碼,在看看執行順序:
////// 按鈕點擊事件 /// /// /// private async void btnStart_Click(object sender, EventArgs e) { //string i1 = await F1Async(); //MessageBox.Show("i1=" + i1); //string i2 = await F2Async(); //MessageBox.Show("i2=" + i2); Tasktask1 = F1Async(); Task task2 = F2Async(); string i1 = await task1; MessageBox.Show("i1=" + i1); string i2 = await task2; MessageBox.Show("i2=" + i2); }
再次運行程序,查看輸出順序:
F1 Start->F2 Start->F1 Run->i1=F1->F2 Run->i2=F2。
可以看出兩次的執行順序不一致。這是什么原因呢?
這是因為并不是到了await才開始執行Task異步任務,執行到Task
我們上面解釋的原因是否正確呢?我們可以做下面的一個實驗,來驗證上面說的原因,我們把按鈕事件里面的await注釋掉,看看異步方法還會不會執行,代碼如下:
////// 按鈕點擊事件 /// /// /// private async void btnStart_Click(object sender, EventArgs e) { //string i1 = await F1Async(); //MessageBox.Show("i1=" + i1); //string i2 = await F2Async(); //MessageBox.Show("i2=" + i2); Tasktask1 = F1Async(); Task task2 = F2Async(); // 并不是到了await才開始執行Task異步任務,這里是保證異步任務一定執行完 // 代碼執行到這里的時候,如果沒有執行完就等待執行完,如果已經執行完,就直接獲取返回值 // 這里注釋掉await代碼段,查看異步方法是否會執行 //string i1 = await task1; //MessageBox.Show("i1=" + i1); //string i2 = await task2; //MessageBox.Show("i2=" + i2); }
運行程序,發現異步方法還是會執行,這就說明我們上面解釋的原因是正確的。感興趣的可以使用Reflector反編譯查看內部實現的原理,主要是MoveNext()方法內部。
我們可以得到下面的結論:
- 只要方法是Task
類型的返回值,都可以用await來等待調用獲取返回值。 - 如果一個返回Task
類型的方法被標記了async,那么只要方法內部直接return T這個類型的實例就可以了。 - 一個返回Task
類型的方法如果沒有被標記為async,那么需要方法內部直接return一個Task的實例。
上面說的第二點看下面的代碼
////// 方法標記為async 直接返回一個int類型的數值即可 /// ///private async Task F3Async() { return 2; }
上面的第三點可以看下面的代碼:
////// 方法沒有被標記為async,直接返回一個Task /// ///private Task F4Async() { return Task.Run (() => { return 2; }); }
二、TPL高級
我們做一些總結:
1、如果方法內部有await,則方法必須標記為async。await和async是成對出現的,只有await沒有async程序會報錯。只有async沒有await,程序會按照同步方法執行。
2、ASP.NET MVC中的Action方法和WinForm中的事件處理方法都可以標記為async,控制臺的Main()方法不能被標記為async。對于不能標記為async的方法怎么辦呢?我們可以使用Result屬性來獲取值,看下面代碼:
using System; using System.Net.Http; using System.Threading.Tasks; namespace AsyncDemo { class Program { static void Main(string[] args) { // 實例化對象 HttpClient client = new HttpClient(); // 調用異步的Get方法 TasktaskMsg = client.GetAsync("http://www.baidu.com"); // 通過Result屬性獲取返回值 HttpResponseMessage msg = taskMsg.Result; Task taskRead = msg.Content.ReadAsStringAsync(); string html = taskRead.Result; Console.WriteLine(html); Console.ReadKey(); } } }
不建議使用這種方式,這樣體現不出異步帶來的好處,而且使用Result屬性,有可能會帶來上下文切換造成的死鎖。
下面我們來看看創建Task的方法。
?1、如果返回值就是一個立即可以隨手得到的值,那么就用Task.FromResult()。看下面代碼:
static TaskTestAsync() { //return Task.Run (() => //{ // return 5; //}); // 簡便寫法 return Task.FromResult(3); }
2、如果是一個需要休息一會的任務(比如下載失敗則過5秒鐘后重試。主線程不休息,和Thread.Sleep不一樣),那么就用Task.Delay()。
3、Task.Factory.FromAsync()會把IAsyncResult轉換為Task,這樣APM風格的API也可以用await來調用。
4、編寫異步方法的簡化寫法。如果方法聲明為async,那么可以直接return具體的值,不用在創建Task,由編譯器創建Task,看下面的代碼:
static async TaskTest2Async() { // 復雜寫法 //return await Task.Run (() => //{ // return 5; //}); // 下面是簡化寫法,直接返回 return 6; }
原文鏈接:https://www.cnblogs.com/dotnet261010/p/12343684.html
相關推薦
- 2022-08-19 React組件通信
- 2022-09-16 python?playwright之元素定位示例詳解_python
- 2022-03-24 Sublime?Text3安裝Go語言相關插件gosublime時搜不到gosublime的解決方法
- 2023-01-10 Flutter?SystemChrome使用方法詳解_Android
- 2022-04-11 Tomcat訪問不到web應用報錯ORA-01882: 未找到時區的解決方案
- 2022-08-12 Python使用Opencv打開筆記本電腦攝像頭報錯解問題及解決_python
- 2022-12-09 Python構造函數與析構函數超詳細分析_python
- 2022-04-12 Oracle?Session每日統計功能實現_oracle
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支