.NET非同步開發筆記(一)_Concurrent vs Parallel computing_執行緒_多執行緒與多工
在程式開發實務上會想要將一段程式改為非同步
不外乎是為了提升整體執行效能又或者響應(回應)速度
(例如:使UI不會卡頓)
Concurrent computing(台灣翻:並行計算/大陸翻:并发计算)
->只有一個處理器(單核心)
數個運算任務同時在特定時間區段內交替地完成,而非依序運行。
通常生活常見可能類似一人多工的感覺
比方一個人負責彈奏2~3種樂器
一個人負責操作兩至三台機台設備
20~30年前早期電腦底層運算就是此模式)
而如今電腦都是多核心了
例如:NodeJs本身單Thread的就一定屬於並行運算
Parallel computing(台灣翻:平行計算/大陸翻:并行计算)
->可能有不只一個處理器(多核心)(效能相對會較高)
許多運算過程會同時發生
通常就軟體層面我們絕大部分就非同步層面開發
是針對「並行計算」的運算架構在進行設計思考與改善,而不會在意平行計算的範疇。
只要先就單個CPU的程式效能改善做好設計,剩下平行的範疇就通通交給作業系統。
大多數軟體開發人員只需要就並行計算做好設計即可,少數情境才需要就平行計算層面做思考和實踐。
所以通常在C#程式中非同步(Asynchronous programming)
大部分是一種Concurrent處理模式(輪流交替地work)
當啟動一非同步任務(背後類似起另一Thread做處理)後將會立即返回至呼叫端
非同步工作必須提出一承諾,告訴未來的狀態與結果。
常見會需要非同步的情境會分
1. I/O Bound
比方呼叫API、存取DB、檔案讀寫、從網路上取一份資料
因為I/O存取過程會導致CPU閒置,使CPU資源會被浪費。
通常可多採用await做等待非同步執行。
2.CPU Bound
比方圖片轉換處理、大數據計算
若程式可能需要進行大量繁重CPU運算,會導致應用程式會處於長期無法回應的狀態。
這類型應用較建議採TPL實作非同步。
備註:ADO.NET寫TSQL方式(I/O Bound)/EF ORM方式(偏向CPU Bound)
同步跟非同步觀念:
一個普通的方法(沒加上async修飾的),通常默認就是同步方法是需要排隊等待依序執行的。
再加上async修飾後則代表我將此方法更改為非同步(異步)方法
此時大部分在撰寫程式時候async 跟await還是感覺跟同步沒捨麼兩樣
依然還是要等待依序的感覺
那觀念上應該要校正為有時還是需要於非同步方法中進行結果等待才往下運行,有時不需要。
非同步方法中裡面運行各個子方法或者任務並非always不要等直接放行,也並非always要等。
在非同步方法中針對要等待的情境
有兩種
第一種.我等其回來並將自己(Main Thread)卡死、鎖住,那就不能執行其他code。
第二種.async / await 等待方法則是把自己的Thread放掉來等你回來結果,你回來之後再次跟OS要一條新的Thread(有可能是原本的那一條也可能是全新另一條)再繼續往下執行。
在.net進行多執行緒開發時候要注意
Thread create完後若裡面設置的call back方法沒有做好try catch是會連動到主執行緒一起嗝屁的
所以通常若用Thread要注意任何call back裡面的方法都要把try catch加好加滿
直到.net4.0後 推出的TPL Task則會自動在底層自動實作try catch
通常初始另外一條Thread若沒有持續監聽其結果
可能會導致許多不如預期的狀況發生
在使用Thread時候也要注意try catch要包在call back事件中
負面程式寫法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ThreadExceptionApp { class Program { //Ref: //Threading Simplified: Part 6 (Exception Handling) //https://www.c-sharpcorner.com/UploadFile/19b1bd/threading-simplified-part-6/ //反面寫法:主程式包try catch不會接獲Thread裡面副程式拋的例外 static void Main(string[] args) { Console.Title = "Threading Exception Handling Demo"; try { new Thread(DoWork).Start(); } catch (Exception ex) { Console.WriteLine("Exception {0}", ex.Message); } } static void DoWork() { throw new ArgumentNullException(); } } } |
修正後寫法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ThreadExceptionApp { class Program { static void Main(string[] args) { Console.Title = "Threading Exception Handling Demo"; new Thread(DoWork).Start(); } static void DoWork() { try { throw new ArgumentNullException(); } catch (Exception ex) { Console.WriteLine("Exception {0}", ex.Message); } } } } |
Thread還有區分前景執行緒/背景執行緒
Foreground Thread(前景執行緒):
通常一個應用程式(Program/Process)可能執行檔跑起來那一條通常習慣稱Main Thread,就是屬於一個Foreground Thread,任何一個Program (Process)其前景執行緒全部都結束主程式才會結束。
若多開另一條Thread 也可以開成前景執行緒(前景or背景可以自己決定)!!
(.net1時期 透過Thread類別產生的Thread默認是前景除非自己調整IsBackground為true)
但通常額外開的另一條Thread大部分都預設背景Thread
若預設啟動起來的Process下只有一條主Thread(前景),若該前景結束
該前景下的背景必定也全會cancel。
若預設啟動起來的Process下不只有一條前景,可能一條主Thread(前景)先開出另一條
背景Thread後又另外開其他條的前景,只要前景沒有全部結束(代表Process還在運行中)
該背景執行緒還會持續運行下去。
Background Thread(背景執行緒):
從執行緒集區(Thread Pool)取得的執行緒,必定是背景執行緒。
(.net4 以後 透過Task類別產生的Thread默認皆必為背景且無法更變為前景)
答案是錯誤的
Process運行起來後必定用到至少一條以上的Thread,Thread結束了Process才會結束。
當一個Process下的所有前景執行緒都結束了,Process才會結束。
多執行緒處理vs多工處理
多執行緒是一種利用單一核心來提供多工處理的能力,用來提供同時執行多條執行緒的工作方式,通常都是由作業系統內建提供的。
多工是指電腦同時執行多個程式的能力。
Ref:
https://gigi.nullneuron.net/gigilabs/common-mistakes-in-asynchronous-programming-with-net/
留言
張貼留言