版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第C#通過(guò)同步和異步實(shí)現(xiàn)優(yōu)化做早餐的時(shí)間目錄概述同步方式做早餐同步做早餐示例同步做早餐示意圖同步方式為何會(huì)【卡住】?異步方式做早餐優(yōu)化異步做早餐優(yōu)化異步早餐示意圖異步異常異步任務(wù)異常示例高效的等待
概述
一天之計(jì)在于晨,每天的早餐也是必不可少,但是很多人為了節(jié)約時(shí)間,都是簡(jiǎn)單的吃點(diǎn)湊合一下或干脆不吃早餐,這對(duì)于個(gè)人身體和工作效率來(lái)說(shuō),無(wú)疑是不合理的,那么要如何做一頓早餐呢?如何能節(jié)約做早餐的時(shí)間呢?本文以一個(gè)簡(jiǎn)單的小例子,簡(jiǎn)述如何做一頓早餐及如何優(yōu)化做早餐的時(shí)間。僅供學(xué)習(xí)分享使用,如有不足之處,還請(qǐng)指正。
正常情況下,做早餐可以分為以下幾個(gè)步驟:
倒一杯咖啡。
加熱平底鍋,然后煎兩個(gè)雞蛋。
煎三片培根。
烤兩片面包。
在烤面包上加黃油和果醬。
倒一杯橙汁。
同步方式做早餐
根據(jù)以上步驟進(jìn)行編程,做一份早餐需要編寫(xiě)程序如下:
///summary
///同步做早餐
////summary
///paramname="sender"/param
///paramname="e"/param
privatevoidbtnBreakfast_Click(objectsender,EventArgse)
this.txtInfo.Clear();
Stopwatchwatch=Stopwatch.StartNew();
watch.Start();
//1.倒一杯咖啡。
stringcup=PourCoffee();
PrintInfo("咖啡沖好了");
//2.加熱平底鍋,然后煎兩個(gè)雞蛋。
stringeggs=FryEggs(2);
PrintInfo("雞蛋煎好了");
//3.煎三片培根。
stringbacon=FryBacon(3);
PrintInfo("培根煎好了");
//4.烤兩片面包。
stringtoast=ToastBread(2);
//5.在烤面包上加黃油和果醬。
ApplyButter(toast);
ApplyJam(toast);
PrintInfo("面包烤好了");
//6.倒一杯橙汁。
stringoj=PourOJ();
PrintInfo("橙汁倒好了");
PrintInfo("早餐準(zhǔn)備完畢!");
watch.Stop();
TimeSpantime=watch.Elapsed;
PrintInfo(string.Format("總運(yùn)行時(shí)間為:{0}秒",time.TotalSeconds.ToString("0.00")));
///summary
///倒一杯咖啡
////summary
///returns/returns
privatestringPourCoffee()
PrintInfo("正在沖咖啡...");
return"咖啡";
///summary
///抹果醬
////summary
///paramname="toast"/param
privatevoidApplyJam(stringtoast)=
PrintInfo("往面包抹果醬");
///summary
///抹黃油
////summary
///paramname="toast"/param
privatevoidApplyButter(stringtoast)=
PrintInfo("往面包抹黃油");
///summary
///烤面包
////summary
///paramname="slices"/param
///returns/returns
privatestringToastBread(intslices)
for(intslice=0;sliceslices;slice++)
PrintInfo("往烤箱里面放面包");
PrintInfo("開(kāi)始烤...");
Task.Delay(3000).Wait();
PrintInfo("從烤箱取出面包");
return"烤面包";
///summary
///煎培根
////summary
///paramname="slices"/param
///returns/returns
privatestringFryBacon(intslices)
PrintInfo($"放{slices}片培根在平底鍋");
PrintInfo("煎第一片培根...");
Task.Delay(3000).Wait();
for(intslice=0;sliceslices;slice++)
PrintInfo("翻轉(zhuǎn)培根");
PrintInfo("煎第二片培根...");
Task.Delay(3000).Wait();
PrintInfo("把培根放盤(pán)子里");
return"煎培根";
///summary
///煎雞蛋
////summary
///paramname="howMany"/param
///returns/returns
privatestringFryEggs(inthowMany)
PrintInfo("加熱平底鍋...");
Task.Delay(3000).Wait();
PrintInfo($"磕開(kāi){howMany}個(gè)雞蛋");
PrintInfo("煎雞蛋...");
Task.Delay(3000).Wait();
PrintInfo("雞蛋放盤(pán)子里");
return"煎雞蛋";
///summary
///倒橙汁
////summary
///returns/returns
privatestringPourOJ()
PrintInfo("倒一杯橙汁");
return"橙汁";
}
同步做早餐示例
通過(guò)運(yùn)行示例,發(fā)現(xiàn)采用同步方式進(jìn)行編程,做一份早餐,共計(jì)15秒鐘,且在此15秒鐘時(shí)間內(nèi),程序處于【卡住】狀態(tài),無(wú)法進(jìn)行其他操作。如下所示:
同步做早餐示意圖
同步方式做早餐,就是一個(gè)做完,再進(jìn)行下一個(gè),順序執(zhí)行,如下所示:
同步方式為何會(huì)【卡住】?
因?yàn)樵诔绦蜻M(jìn)程中,會(huì)有一個(gè)主線程,用于響應(yīng)用戶的操作,同步方式下,做早餐的和前端頁(yè)面同在主線程中,所以當(dāng)開(kāi)始做早餐時(shí),就不能響應(yīng)其他的操作了。這就是【兩耳不聞窗外事,一心只讀圣賢書(shū)】的境界。但如果讓用戶長(zhǎng)時(shí)間處于等待狀態(tài),會(huì)讓用戶體驗(yàn)很不友好。比如,劉玄德三顧茅廬,大雪紛飛之下,諸葛亮在草廬中午睡,劉關(guān)張?jiān)诖笱┲徐o等。試問(wèn)有幾人會(huì)有玄德的耐心,何況程序也不是諸葛亮,用戶也沒(méi)有玄德的耐心!
異步方式做早餐
上述代碼演示了不正確的實(shí)踐:構(gòu)造同步代碼來(lái)執(zhí)行異步操作。顧名思義,此代碼將阻止執(zhí)行這段代碼的線程執(zhí)行任何其他操作。在任何任務(wù)進(jìn)行過(guò)程中,此代碼也不會(huì)被中斷。就如同你將面包放進(jìn)烤面包機(jī)后盯著此烤面包機(jī)一樣。你會(huì)無(wú)視任何跟你說(shuō)話的人,直到面包彈出。如何做才能避免線程阻塞呢?答案就是異步。await關(guān)鍵字提供了一種非阻塞方式來(lái)啟動(dòng)任務(wù),然后在此任務(wù)完成時(shí)繼續(xù)執(zhí)行。
首先更新代碼,對(duì)于耗時(shí)的程序,采用異步方式做早餐,如下所示:
privateasyncvoidbtnBreakfastAsync_Click(objectsender,EventArgse)
this.txtInfo.Clear();
Stopwatchwatch=Stopwatch.StartNew();
watch.Start();
//1.倒一杯咖啡。
stringcup=PourCoffee();
PrintInfo("咖啡沖好了");
//2.加熱平底鍋,然后煎兩個(gè)雞蛋。
//Taskstringeggs=FryEggsAsync(2);
stringeggs=awaitFryEggsAsync(2);
PrintInfo("雞蛋煎好了");
//3.煎三片培根。
stringbacon=awaitFryBaconAsync(3);
PrintInfo("培根煎好了");
//4.烤兩片面包。
stringtoast=awaitToastBreadAsync(2);
//5.在烤面包上加黃油和果醬。
ApplyButter(toast);
ApplyJam(toast);
PrintInfo("面包烤好了");
//6.倒一杯橙汁。
stringoj=PourOJ();
PrintInfo("橙汁倒好了");
PrintInfo("早餐準(zhǔn)備完畢!");
watch.Stop();
TimeSpantime=watch.Elapsed;
PrintInfo(string.Format("總運(yùn)行時(shí)間為:{0}秒",time.TotalSeconds.ToString("0.00")));
///summary
///異步烤面包
////summary
///paramname="slices"/param
///returns/returns
privateasyncTaskstringToastBreadAsync(intslices)
for(intslice=0;sliceslices;slice++)
PrintInfo("往烤箱里面放面包");
PrintInfo("開(kāi)始烤...");
awaitTask.Delay(3000);
PrintInfo("從烤箱取出面包");
return"烤面包";
///summary
///異步煎培根
////summary
///paramname="slices"/param
///returns/returns
privateasyncTaskstringFryBaconAsync(intslices)
PrintInfo($"放{slices}片培根在平底鍋");
PrintInfo("煎第一片培根...");
awaitTask.Delay(3000);
for(intslice=0;sliceslices;slice++)
PrintInfo("翻轉(zhuǎn)培根");
PrintInfo("煎第二片培根...");
awaitTask.Delay(3000);
PrintInfo("把培根放盤(pán)子里");
return"煎培根";
///summary
///異步煎雞蛋
////summary
///paramname="howMany"/param
///returns/returns
privateasyncTaskstringFryEggsAsync(inthowMany)
PrintInfo("加熱平底鍋...");
awaitTask.Delay(3000);
PrintInfo($"磕開(kāi){howMany}個(gè)雞蛋");
PrintInfo("煎雞蛋...");
awaitTask.Delay(3000);
PrintInfo("雞蛋放盤(pán)子里");
return"煎雞蛋";
}
注意:通過(guò)測(cè)試發(fā)現(xiàn),異步方式和同步方式的執(zhí)行時(shí)間一致,所以采用異步方式并不會(huì)縮短時(shí)間,但是程序已不再阻塞,可以同時(shí)響應(yīng)用戶的其他請(qǐng)求。
優(yōu)化異步做早餐
通過(guò)上述異步方式,雖然優(yōu)化了程序,不再阻塞,但是時(shí)間并沒(méi)有縮短,那么要如何優(yōu)化程序來(lái)縮短時(shí)間,以便早早的吃上可口的早餐呢?答案就是在開(kāi)始一個(gè)任務(wù)后,在等待任務(wù)完成時(shí),可以繼續(xù)進(jìn)行準(zhǔn)備其他的任務(wù)。你也幾乎將在同一時(shí)間完成所有工作。你將吃到一頓熱氣騰騰的早餐。通過(guò)合并任務(wù)和調(diào)整任務(wù)的順序,將大大節(jié)約任務(wù)的完成時(shí)間,如下所示:
///summary
///優(yōu)化異步做早餐
////summary
///paramname="sender"/param
///paramname="e"/param
privateasyncvoidbtnBreakfast2_Click(objectsender,EventArgse)
this.txtInfo.Clear();
Stopwatchwatch=Stopwatch.StartNew();
watch.Start();
//1.倒一杯咖啡。
stringcup=PourCoffee();
PrintInfo("咖啡沖好了");
//2.加熱平底鍋,然后煎兩個(gè)雞蛋。
TaskstringeggsTask=FryEggsAsync(2);
//3.煎三片培根。
TaskstringbaconTask=FryBaconAsync(3);
//4.5合起來(lái)烤面包,抹果醬,黃油
TaskstringtoastTask=MakeToastWithButterAndJamAsync(2);
stringeggs=awaiteggsTask;
PrintInfo("雞蛋煎好了");
stringbacon=awaitbaconTask;
PrintInfo("培根煎好了");
stringtoast=awaittoastTask;
PrintInfo("面包烤好了");
//6.倒一杯橙汁。
stringoj=PourOJ();
PrintInfo("橙汁倒好了");
PrintInfo("早餐準(zhǔn)備完畢!");
watch.Stop();
TimeSpantime=watch.Elapsed;
PrintInfo(string.Format("總運(yùn)行時(shí)間為:{0}秒",time.TotalSeconds.ToString("0.00")));
///summary
///組合任務(wù)
////summary
///paramname="number"/param
///returns/returns
privateasyncTaskstringMakeToastWithButterAndJamAsync(intnumber)
vartoast=awaitToastBreadAsync(number);
ApplyButter(toast);
ApplyJam(toast);
returntoast;
}
在本例中,合并了【烤面包+抹果醬+抹黃油】為一個(gè)任務(wù),這樣是烤面包的同時(shí),可以煎雞蛋,煎培根,三項(xiàng)耗時(shí)任務(wù)同時(shí)執(zhí)行。在三個(gè)任務(wù)都完成是,早餐也就做好了,示例如下所示:
通過(guò)以上優(yōu)化示例發(fā)現(xiàn),通過(guò)合并任務(wù)和調(diào)整順序,做一份早餐,需要6.06秒。
優(yōu)化異步早餐示意圖
優(yōu)化后的異步做早餐,由于一些任務(wù)并發(fā)運(yùn)行,因此節(jié)約了時(shí)間。示意圖如下所示:
異步異常
上述示例假定所有的任務(wù)都可以正常完成,那么如果某一個(gè)任務(wù)執(zhí)行過(guò)程中發(fā)生了異常,要如何捕獲呢?答案是:當(dāng)任務(wù)無(wú)法成功完成時(shí),它們將引發(fā)異常。當(dāng)啟動(dòng)的任務(wù)為awaited時(shí),客戶端代碼可捕獲這些異常。
例如當(dāng)烤面包的時(shí)候,烤箱突然著火了,如何處理異常呢?代碼如下所示:
privateasyncvoidbtnBreakfastAsync3_Click(objectsender,EventArgse)
this.txtInfo.Clear();
Stopwatchwatch=Stopwatch.StartNew();
watch.Start();
//1.倒一杯咖啡。
stringcup=PourCoffee();
PrintInfo("咖啡沖好了");
//2.加熱平底鍋,然后煎兩個(gè)雞蛋。
TaskstringeggsTask=FryEggsAsync(2);
//3.煎三片培根。
TaskstringbaconTask=FryBaconAsync(3);
//4.5合起來(lái)烤面包,抹果醬,黃油
TaskstringtoastTask=MakeToastWithButterAndJamAsyncEx(2);
stringeggs=awaiteggsTask;
PrintInfo("雞蛋煎好了");
stringbacon=awaitbaconTask;
PrintInfo("培根煎好了");
stringtoast=awaittoastTask;
PrintInfo("面包烤好了");
//6.倒一杯橙汁。
stringoj=PourOJ();
PrintInfo("橙汁倒好了");
PrintInfo("早餐準(zhǔn)備完畢!");
watch.Stop();
TimeSpantime=watch.Elapsed;
PrintInfo(string.Format("總運(yùn)行時(shí)間為:{0}秒",time.TotalSeconds.ToString("0.00")));
catch(AggregateExceptionex){
PrintInfo("線程內(nèi)部異常");
PrintInfo(ex.StackTrace);
catch(Exceptionex)
PrintInfo("其他異常");
PrintInfo(ex.Message);
///summary
///組合任務(wù)
////summary
///paramname="number"/param
///returns/returns
privateasyncTaskstringMakeToastWithButterAndJamAsyncEx(intnumber)
vartoast=awaitToastBreadAsyncEx(number);
ApplyButter(toast);
ApplyJam(toast);
returntoast;
///summary
///異步烤面包異常
////summary
///paramname="slices"/param
///returns/returns
privateasyncTaskstringToastBreadAsyncEx(intslices)
for(intslice=0;sliceslices;slice++)
PrintInfo("往烤箱里面放面包");
PrintInfo("開(kāi)始烤...");
awaitTask.Delay(2000);
PrintInfo("著火了!面包糊了!");
inta=1,b=0;
inti=a/b;//制造一個(gè)異常
//thrownewInvalidOperationException("烤箱著火了!");
awaitTask.Delay(1000);
PrintInfo("從烤箱取出面包");
return"烤面包";
}
異步任務(wù)異常示例
請(qǐng)注意,從烤面包機(jī)著火到發(fā)現(xiàn)異常,有相當(dāng)多的任務(wù)要完成。當(dāng)異步運(yùn)行的任務(wù)引發(fā)異常時(shí),該任務(wù)出錯(cuò)。Task對(duì)象包含Task.Exception屬性中引發(fā)的異常。出錯(cuò)的任務(wù)在等待時(shí)引發(fā)異常。
需要理解兩個(gè)重要機(jī)制:異常在出錯(cuò)的任務(wù)中的存儲(chǔ)方式,以及在代碼等待出錯(cuò)的任務(wù)時(shí)解包并重新引發(fā)異常的方式。
當(dāng)異步運(yùn)行的代碼引發(fā)異常時(shí),該異常存儲(chǔ)在Task中。Task.Exception屬性為System.AggregateException,因?yàn)楫惒焦ぷ髌陂g可能會(huì)引發(fā)多個(gè)異常。引發(fā)的任何異常都將添加到AggregateException.InnerExceptions集合中。如果該Exception屬性為NULL,則將創(chuàng)建一個(gè)新的AggregateException且引發(fā)的異常是該集合中的第一項(xiàng)。
對(duì)于出錯(cuò)的任務(wù),最常見(jiàn)的情況是Exception屬性只包含一個(gè)異常。當(dāng)代碼awaits出錯(cuò)的任務(wù)時(shí),將重新引發(fā)AggregateException.InnerExceptions集合中的第一個(gè)異常。因此,此示例的輸出顯示InvalidOperationException而不是AggregateException。提取第一個(gè)內(nèi)部異常使得使用異步方法與使用其對(duì)應(yīng)的同步方法盡可能相似。當(dāng)你的場(chǎng)景可能生成多個(gè)異常時(shí),可在代碼中檢查Exception屬性。
高效的等待
通過(guò)以上示例,需要等待很多任務(wù)完成,然后早餐才算做好,那么如何才能高效優(yōu)雅的等待呢?可以通過(guò)使用Task類(lèi)的方法改進(jìn)上述代碼末尾的一系列await語(yǔ)句。其中一個(gè)API是WhenAll,它將返回一個(gè)其參數(shù)列表中的所有任務(wù)都已完成時(shí)才完成的Task,如下所示:
privateasyncvoidbtnBreakfastAsync4_Click(objectsender,EventArgse)
this.txtInfo.Clear();
Stopwatchwatch=Stopwatch.StartNew();
watch.Start();
//1.倒一杯咖啡。
stringcup=PourCoffee();
PrintInfo("咖啡沖好了");
//2.加熱平底鍋,然后煎兩個(gè)雞蛋。
TaskstringeggsTask=FryEggsAsync(2);
//3.煎三片培根。
TaskstringbaconTask=FryBaconAsync(3);
//4.5合起來(lái)烤面包,抹果醬,黃油
TaskstringtoastTask=MakeToastWithButterAndJamAsync(2);
//等待任務(wù)完成
awaitTask.WhenAll(eggsTask,baconTask,toastTask);
PrintInfo("雞蛋煎好了");
PrintInfo("培根煎好了");
PrintInfo("面包烤好了");
//6.倒一杯橙汁。
stringoj=PourOJ();
PrintInfo("橙汁倒好了");
PrintInfo("早餐準(zhǔn)備完畢!");
watch.Stop();
TimeSpantime=watch.Elapsed;
PrintInfo(string.Format("總運(yùn)行時(shí)間為:{0}秒",time.TotalSeconds.ToString("0.00")));
}
另一種選擇是使用WhenAny,它將返回一個(gè)當(dāng)其參數(shù)完
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2026年湖南勞動(dòng)人事職業(yè)學(xué)院?jiǎn)握新殬I(yè)傾向性測(cè)試題庫(kù)及答案1套
- 2026年哈爾濱應(yīng)用職業(yè)技術(shù)學(xué)院?jiǎn)握新殬I(yè)適應(yīng)性測(cè)試模擬測(cè)試卷及答案1套
- 2025廣東省疾病預(yù)防控制中心招聘科研助理1人(公共基礎(chǔ)知識(shí))測(cè)試題附答案
- 2026寧波市江北區(qū)面向2026屆高校畢業(yè)生招聘高層次和緊缺人才13人筆試參考題庫(kù)及答案解析
- 2025年甘肅省定西市隴西縣福星中心衛(wèi)生院高塄分院招聘鄉(xiāng)村醫(yī)生(公共基礎(chǔ)知識(shí))綜合能力測(cè)試題附答案
- 2026中國(guó)安能集團(tuán)科工有限公司招聘6人筆試參考題庫(kù)及答案解析
- 2025河南省人力資源開(kāi)發(fā)中心有限公司招聘1人考試題庫(kù)附答案
- 2025年甘肅省隴南師范學(xué)院第二批高層次人才和急需緊缺專(zhuān)業(yè)技術(shù)人才引進(jìn)(20人)參考題庫(kù)附答案
- 2025廣東廣州市天河區(qū)靈秀小學(xué)招聘英語(yǔ)教師1人(學(xué)校自籌經(jīng)費(fèi))考試歷年真題匯編附答案
- 2025年保山市部分醫(yī)療衛(wèi)生事業(yè)單位招聘博士研究生(10人)筆試備考題庫(kù)附答案
- 廣東省大灣區(qū)2023-2024學(xué)年高一上學(xué)期期末生物試題【含答案解析】
- 第四單元地理信息技術(shù)的應(yīng)用課件 【高效課堂+精研精講】高中地理魯教版(2019)必修第一冊(cè)
- 魯科版高中化學(xué)必修一教案全冊(cè)
- 提高隧道初支平整度合格率
- 2023年版測(cè)量結(jié)果的計(jì)量溯源性要求
- 建筑能耗與碳排放研究報(bào)告
- GB 29415-2013耐火電纜槽盒
- 中國(guó)古代經(jīng)濟(jì)試題
- 軟件定義汽車(chē):產(chǎn)業(yè)生態(tài)創(chuàng)新白皮書(shū)
- 磷石膏抹灰專(zhuān)項(xiàng)施工方案
- 水電水利工程施工質(zhì)量管理培訓(xùn)講義
評(píng)論
0/150
提交評(píng)論