商城網站建設專業(yè)公司怎么查百度競價關鍵詞價格
目錄
- 1 線程與進程
- 2 創(chuàng)建線程
- 3 線程等待
- 4 線程優(yōu)先級
- 5 前臺線程與后臺線程
- 6 Lock與線程安全
- 7 Monitor
- 8 死鎖
- 9 線程中異常處理
1 線程與進程
進程是計算機概念,一個程序運用時占用的的所有計算機資源(CPU、內存、硬盤、網絡)統(tǒng)稱為進程。
線程是操作系統(tǒng)中能夠獨立運行的最小單位,是進程(包含多個線程)中的一部分,線程也有自己的計算資源,多個線程間可以共享進程的資源
C#中的Thread其實是對計算機中線程概念的封裝(API的封裝),它的執(zhí)行歸根結底是向底層操作系統(tǒng)申請了線程資源。
在C#中的線程實現(xiàn)包括 Thread/ThreadPool/Task/Await Async
多線程的本質是資源換性能(CPU、內存、硬盤、網絡)。好處是提高利用率(快) 壞處是耗費資源。但是在使用N個線程時,性能并不是成N倍的遞增。
因為操作系統(tǒng)對CPU的調度時必須耗費些許時間,包括CPU的分片,線程的調度以及上下文的切換。同時CPU不夠時降低線程的性能,這也意味著不是線程越多越好。
以下代碼示例 默認引用了System.Threading命名空間
2 創(chuàng)建線程
線程創(chuàng)建 可以不通過顯示的調用ThreadStart和ParameterizedThreadStart,主要是通過lamda表達式去創(chuàng)建線程
static void Main(string[] args){Console.OutputEncoding = Encoding.UTF8;//public Thread(ThreadStart start) ThreadStart 一個無參數(shù)的委托Thread t1 = new Thread(() =>{Console.WriteLine("一個無參數(shù)的的線程創(chuàng)建");});t1.Start();//Thread(ParameterizedThreadStart start) ParameterizedThreadStart 一個有參數(shù)的委托//lambda 表達式是向線程傳遞數(shù)據(jù)的最強大的方法。然而必須小心,不要在啟動線程之后誤修改被捕獲變量(captured variables).解決方法就是使用臨時變量Thread t2 = new Thread((x) =>{Console.WriteLine("一個有參數(shù)的的線程創(chuàng)建{0}", x);});//傳入?yún)?shù)t2.Start(100);}
3 線程等待
可以使用 Join()方法來等待其他線程完成任務
public class ThreadWork{public void Method1() {Console.OutputEncoding = Encoding.UTF8;Console.WriteLine("當前線程名稱" + Thread.CurrentThread.Name);for (int i = 0; i < 10; i++) {Thread.Sleep(1000);//線程暫停Console.WriteLine(i);}}} public class Program{static void Main(string[] args){ThreadWork work = new ThreadWork();Thread t = new Thread(work.Method1);t.Start();t.Join();//主線程等待子線程完成任務Console.WriteLine("Finish");}
}
4 線程優(yōu)先級
線程的Priority屬性決定了相對于操作系統(tǒng)中的其它活動線程,它可以獲得多少執(zhí)行時間。線程優(yōu)先級的取值如下:
enum ThreadPriority { Lowest, BelowNormal, Normal, AboveNormal, Highest } 。只有當多個線程同時活動時,線程優(yōu)先級才有意義。默認創(chuàng)建的線程優(yōu)先級別為Normal
public class ThreadWork{public void Method1() {Console.OutputEncoding = Encoding.UTF8;Console.WriteLine("當前線程名稱" + Thread.CurrentThread.Name);for (int i = 0; i < 10; i++) {Thread.Sleep(1000);//線程暫停Console.WriteLine(i);}}}
static void Main(string[] args){ThreadWork work = new ThreadWork();Thread t1 = new Thread(work.Method1) { Priority = ThreadPriority.Highest, Name = "t1" };Thread t2 = new Thread(work.Method1) { Priority = ThreadPriority.Normal, Name = "t2" };t2.Start(); //t1線程優(yōu)先級別高于t2線程,將優(yōu)先獲得CPU的時間片,先執(zhí)行t1.Start();Console.WriteLine("Finish");}
5 前臺線程與后臺線程
可以通過設置IsBackground來設定線程是否為后臺線程 。默認顯示創(chuàng)建的線程都是前臺線程。前臺線程與后臺線程最大的區(qū)別就是 進程會等待所有的前臺線程,當前臺線程全部結束后,盡管后臺線程的任務沒有執(zhí)行完,進程會自動結束所有存在的后臺線程。所以當程式定義了一個永遠不會完成的前臺線程時,進程將永遠不會結束。線程的前臺/后臺狀態(tài)與它的優(yōu)先級和執(zhí)行時間的分配無關
public class ThreadWork{public void Method2(int num) {Console.OutputEncoding = Encoding.UTF8;for (int i = 0; i < num; i++){Thread.Sleep(1000);//線程暫停Console.WriteLine("當前線程名稱{0} 打印數(shù)據(jù){1}", Thread.CurrentThread.Name,i);}}}
static void Main(string[] args){ThreadWork work = new ThreadWork();Thread t1 = new Thread(() => work.Method2(5)) { Name = "t1" };Thread t2 = new Thread(() => work.Method2(10)) { Name = "t2", IsBackground = true };t1.Start();t2.Start(); // t1線程為前臺線程(執(zhí)行時間>10s) t2線程為后臺線程(執(zhí)行時間>5s) 前臺線程結束后,會中斷t2線程Console.WriteLine("Finish");}
6 Lock與線程安全
競態(tài): 字面意思是競爭,并發(fā)的執(zhí)行單元對共享資源(硬件資源和軟件上的全局變量,靜態(tài)變量等)的訪問容易發(fā)生競態(tài),競態(tài)是出現(xiàn)線程不安全的重要因素。
為了解決多線程并發(fā)搶奪共享資源的問題,可采用lock 關鍵字,lock會鎖定最先搶到資源的線程,并且會阻塞(不占用CPU資源)其他試圖搶奪該資源的其他線程,直到lock鎖定的線程釋放這個鎖資源
這樣就確保了在同一時刻只有一個線程能進入臨界區(qū)(critical section,不允許并發(fā)執(zhí)行的代碼)像這種用來避免在多線程下的不確定性的方式被稱為線程安全(thread-safe)
盡管lock關鍵字的存在會解決線程安全的問題,但是運用不恰當,存在嚴重的性能問題,因為上下切換上下文的時間可能會比單個線程執(zhí)行還要慢。
public class CounterBase{//多線程共享這個變量,當存在讀寫操作時,會出現(xiàn)線程不安全的問題public int num { get; private set; }public CounterBase(int num){this.num = num;}public void DecrementWithLock(){Console.WriteLine(Thread.CurrentThread.Name);//使用lock關鍵字,標識這段方法只允許一個線程持有lock (this) {num--;}}public void IncrementWithLock(){Console.WriteLine(Thread.CurrentThread.Name);lock (this){num++;}}public void Count (){for (int i = 0; i < 10; i++){IncrementWithLock();DecrementWithLock();}}}
static void Main(string[] args){CounterBase counter = new CounterBase(100);Thread t1 = new Thread(() => counter.Count()) { Name = "t1" }; // t1、t2 線程共享counter實例,并同時執(zhí)行CountWhihLock()方法。意味著此時出現(xiàn)了競態(tài)現(xiàn)象,容易出現(xiàn)線程不安全的問題 Thread t2 = new Thread(() => counter.Count()) { Name = "t2" };//多線程并行t1.Start();t2.Start();t1.Join();t2.Join();Console.WriteLine("Finish");Console.WriteLine(counter.num); //100}
7 Monitor
在Lock與線程安全這一section中,用到lock關鍵字來確保代碼屏障區(qū)(用lock關鍵字包裹的代碼區(qū),同一時間,只有一個線程能進入到代碼中)的安全執(zhí)行 。lock關鍵字Monitor類的的一個語法糖??梢酝ㄟ^TryEnter()進入鎖 Monitor.Exit()進出鎖。上述示例可以用Monitor類來重構功能
public class CounterBase{//多線程共享這個變量,當存在讀寫操作時,會出現(xiàn)線程不安全的問題public int num { get; private set; }public CounterBase(int num){this.num = num;}public void DecrementWithLock(){Console.WriteLine(Thread.CurrentThread.Name);//使用Monitor替換lock關鍵字Monitor.TryEnter();num++;Monitor.Exit();}public void IncrementWithLock(){Console.WriteLine(Thread.CurrentThread.Name);//使用Monitor替換lock關鍵字Monitor.TryEnter();num--;Monitor.Exit();}public void Count (){for (int i = 0; i < 10; i++){IncrementWithLock();DecrementWithLock();}}}
8 死鎖
死鎖 是多個線程等待永遠拿不到的鎖資源時 出現(xiàn)的程式異常問題。線程處于阻塞狀態(tài)中。
public class DeadLockWork{private readonly object _lock1 = new object();private readonly object _lock2 = new object();public void Method1() {lock (_lock1) {Console.WriteLine(Thread.CurrentThread.Name+" enterd Method1");Thread.Sleep(10);lock (_lock2) {}}}public void Method2(){lock (_lock2){Console.WriteLine(Thread.CurrentThread.Name + " enterd Method2");Thread.Sleep(10);lock (_lock1){}}}}
static void Main(string[] args){DeadLockWork deadLock = new DeadLockWork();// t1、t2 線程共享deadLock實例Thread t1 = new Thread(() => deadLock.Method1()) { Name = "t1" };Thread t2 = new Thread(() => deadLock.Method2()) { Name = "t2" };t1.Start();//t1線程進入方法后,持有_lock1鎖,暫停10ms后嘗試獲取_lock2鎖t2.Start();//t2線程進入方法后,持有_lock2鎖,暫停10ms后嘗試獲取_lock1鎖//主線程等待t1、t2線程完成任務t1.Join();t2.Join();//由于lock關鍵字會阻塞其他正在嘗試獲取鎖的線程,t1持有l(wèi)ock1并嘗試獲取_lock2鎖,t2持有l(wèi)ock2并嘗試獲取_lock1鎖。//t1永遠拿不到_lock2鎖,t2永遠拿不到_lock1鎖。導致線程死鎖。//進程等待兩個前臺線程執(zhí)行完成,死鎖導致程式永遠不會結束Console.WriteLine("Finish");}
9 線程中異常處理
在實際項目中,每一個線程中應該有異常處理的邏輯,
因為每個線程都獨享??臻g,主線程無法捕獲其他工作線程中拋出的異常。未被捕獲的異??赡軙е鲁淌降漠惓V袛唷榱思訌姵淌降慕研?#xff0c;應該在工作任務中設計異常處理的邏輯
static void Main(string[] args){Console.OutputEncoding = Encoding.UTF8;Thread t1 = new Thread(() =>{try{Console.WriteLine("I am Thread " + Thread.CurrentThread.Name);throw new Exception("模擬工作的中的異常:端口被占用");}catch (Exception e){//捕獲線程中的異常Console.WriteLine(e.Message);}}){ Name = "t1" };t1.Start();}