什麼是多線程?
多線程就是使程序並發(同時)執行幾個操作。
.NET 框架類庫在System.Threading 中加入了多線程的能力。因此要在前面加入引用 using System.Threading
Thread 類:創建並控制線程,設置其優先順序並獲取其狀態。
Thread 類的構造方法,主要有2中:Thread thread_name=
Thread(ThreadStart):ThreadStart 委託,它表示此線程開始執行時要調用的方法。適用於無參數的方法。
Thread(ParameterizedThreadStart):ParameterizedThreadStart 委託,它表示此線程開始執行時要調用的方法。適用於有參數傳入的方法。
一個普通的 無參 線程操作如下:
[csharp] view plaincopyprint? 01.Thread td = new Thread(xunhuan);//定義一個線程,參數是一個方法,無返回值,採用的是委託 02. 03.//前台線程,所有的線程都執行完了,應用程序才退出,默認的都是前台線程 04.//後台線程,所有的前台線程都執行完了,就退出,不管後台的線程 05.td.IsBackground = true; //設定為後台線程 06.td.Start();//啟動線程 Thread td = new Thread(xunhuan);//定義一個線程,參數是一個方法,無返回值,採用的是委託 //前台線程,所有的線程都執行完了,應用程序才退出,默認的都是前台線程 //後台線程,所有的前台線程都執行完了,就退出,不管後台的線程 td.IsBackground = true; //設定為後台線程 td.Start();//啟動線程 一個有參的線程如下:[csharp] view plaincopyprint? 01.Thread ptd = new Thread(showname);//定義個線程,傳入的帶參數的方法。 02.ptd.IsBackground = true; 03.ptd.Start("lilei");//重載Start方法,傳遞個參數 Thread ptd = new Thread(showname);//定義個線程,傳入的帶參數的方法。 ptd.IsBackground = true; ptd.Start("lilei");//重載Start方法,傳遞個參數 有參的方法定義,參數objec類型,[csharp] view plaincopyprint? 01.//線程調用,帶多個參數 02. static void shownames(object names) 03. { 04. List list = names as List; 05. foreach (string name in list) 06. { 07. MessageBox.Show(name); 08. } 09. 10. } //線程調用,帶多個參數 static void shownames(object names) { List list = names as List; foreach (string name in list) { MessageBox.Show(name); } } ------------------------------------------------------------------------線程的狀態----------------------------------------------------------------------- 任何時候,線程都要處於某種線程狀態中。 新線程在Unstarted狀態中開始它的生命周期。在調用Thread類的Start方法之前,會一直保持在Unstarted狀態,調用方法之後,就會進入Started狀態,並立即將程序的控制權返回調用程序(點了線程調用後,可以立即去干別的事)。然後,調用了Start方法的線程(也就是Started線程)和程序中其他的線程並發執行。 線程的優先順序 每個線程都有個優先順序,其範圍在ThreadPriority.Lowest和ThreadPriority.Highest之間。默認情況下,每個線程的優先順序都是Normal。 Windows操作系統支持時間分片(timeslicing)的概念,它的思路是優先順序相同的線程共享一個處理器。 ------------------------------------------------------------------------線程的同步和類監視器----------------------------------------------------------------------- 通常,多個執行線程要操作共享數據。如果有權訪問共享數據的線程只能讀取數據,那就不需要阻止多個線程同時訪問共享數據。然而,當多個線程共享數據,並且其中一個或多個線程要修改數據時,可能會出現無法預知的結果。如果一個線程正在更新數據,另一個線程也試圖更新,那麼數據所反映的就第二次更新操作之後的結果。 所以可通過一次只允許一個線程訪問用於操作共享數據的代碼來解決。其他想要操作數據的線程應該等待。具有排他訪問權的線程完成對數據的操作後,等待操作線程的數據可以繼續執行。這稱為互斥或線程同步。 C#提供了兩中解決技術: 1.Monitor類:主要方法(方法傳入的參數為objec對象,一般為當前調用的線程): Monitor.Enter():獲取排他鎖。 Monitor.Wait():釋放對象上的鎖並阻止當前線程,直到重新獲取該鎖。 Monitor.Pulse():通知等待隊列中的線程對象狀態的改變。 Monitor.Exit():釋放排他鎖。 2.lock關鍵字: 在對象前加個lock: 代碼示例:Monitor的用法:[csharp] view plaincopyprint? 01.public class mt 02. { 03. private int age; 04. private int buff = 0;//buff判斷內容是否已被更新或提取,0為未更新,1為已更新 05. int Age 06. { 07. get 08. { 09. Monitor.Enter(this);//獲取此對象的排他鎖 10. if (buff == 0)//若內容為空或未更新就使此線程等待 11. { 12. MessageBox.Show("內容為空或未更新"); 13. Monitor.Wait(this);//釋放鎖並等待 14. } 15. buff--; 16. MessageBox.Show("讀取內容"); 17. Monitor.Pulse(this); //通知等待隊列的線程,此對象狀態要更改 18. Monitor.Exit(this);//釋放排他鎖 19. return age; 20. 21. } 22. set 23. { 24. Monitor.Enter(this); 25. if (buff == 1) 26. { 27. MessageBox.Show("內容還沒被讀取"); 28. Monitor.Wait(this); 29. } 30. buff++; 31. MessageBox.Show("寫入內容"+value); 32. age = value; 33. Monitor.Pulse(this); 34. Monitor.Exit(this); 35. } 36. } 37. } public class mt { private int age; private int buff = 0;//buff判斷內容是否已被更新或提取,0為未更新,1為已更新 int Age { get { Monitor.Enter(this);//獲取此對象的排他鎖 if (buff == 0)//若內容為空或未更新就使此線程等待 { MessageBox.Show("內容為空或未更新"); Monitor.Wait(this);//釋放鎖並等待 } buff--; MessageBox.Show("讀取內容"); Monitor.Pulse(this); //通知等待隊列的線程,此對象狀態要更改 Monitor.Exit(this);//釋放排他鎖 return age; } set { Monitor.Enter(this); if (buff == 1) { MessageBox.Show("內容還沒被讀取"); Monitor.Wait(this); } buff++; MessageBox.Show("寫入內容"+value); age = value; Monitor.Pulse(this); Monitor.Exit(this); } } } lock的用法:[csharp] view plaincopyprint? 01.//lock的用法 02. int Age2 03. { 04. get 05. { 06. lock(this) //開始階段,自獲取了排他鎖 07. { 08. if (buff == 0)//若內容為空或未更新就使此線程等待 09. { 10. MessageBox.Show("內容為空或未更新"); 11. Monitor.Wait(this);//釋放鎖並等待 12. } 13. buff--; 14. MessageBox.Show("讀取內容"); 15. Monitor.Pulse(this); //通知等待隊列的線程,此對象狀態要更改 16. return age; 17. 18. }//結束階段,釋放了排他鎖 19. } 20. set 21. { 22. lock (this) 23. { 24. if (buff == 1) 25. { 26. MessageBox.Show("內容還沒被讀取"); 27. Monitor.Wait(this); 28. } 29. buff++; 30. MessageBox.Show("寫入內容" + value); 31. age = value; 32. Monitor.Pulse(this); 33. } 34. } 35. 36. } //lock的用法 int Age2 { get { lock(this) //開始階段,自獲取了排他鎖 { if (buff == 0)//若內容為空或未更新就使此線程等待 { MessageBox.Show("內容為空或未更新"); Monitor.Wait(this);//釋放鎖並等待 } buff--; MessageBox.Show("讀取內容"); Monitor.Pulse(this); //通知等待隊列的線程,此對象狀態要更改 return age; }//結束階段,釋放了排他鎖 } set { lock (this) { if (buff == 1) { MessageBox.Show("內容還沒被讀取"); Monitor.Wait(this); } buff++; MessageBox.Show("寫入內容" + value); age = value; Monitor.Pulse(this); } } } -----------------------------------------------------------------lock 與 Monitor 的注意事項------------------------------------------------------------------ Monitor注意的地方:用Monitor類的Enter方法和Exit方法來管理對象的鎖時,要想釋放鎖,必須顯示調用Exit方法。調用Exit方法之前如果某個方法中引發了一個異常,並且這個異常沒有被捕捉到,方法就會終止,而且不會調用Exit方法。因此鎖沒有被釋放。為了避免這種錯誤,可以將可能引發異常的代碼放入一個try模塊,並將對Exit方法的調用放在相應的finally塊上以確保釋放鎖。 使用一個Lock塊來管理同步對象上的鎖,可以避免忘記調用Monitor類的Exit方法來釋放鎖。Lock處於某種原因二終止時,C#會隱式調用Monitor類的exit方法。如此一來,即使在代碼中出現異常,也可以將鎖釋放