線程是怎樣工作的 1.多線程由一個線程調度器來進行內部管理,一個功能是CLR常常委托給操做系統。 一個線程調度器確保所有激活的線程在執行期間被合適的分配,等待或者阻塞的線程(比如,一個獨占鎖或者等待用戶輸入)不占用CPU資源。 2.在單核電腦上,一個線程調度器讓時間片在每一個激活的線程中切換。在wi ...
線程是怎樣工作的
1.多線程由一個線程調度器來進行內部管理,一個功能是CLR常常委托給操做系統。
一個線程調度器確保所有激活的線程在執行期間被合適的分配,等待或者阻塞的線程(比如,一個獨占鎖或者等待用戶輸入)不占用CPU資源。
2.在單核電腦上,一個線程調度器讓時間片在每一個激活的線程中切換。在windows操作系統下,線程切換的時間分片通常為10微秒,遠遠大於CPU的開銷時間(通常小於1微秒)。
3.在一個多核的電腦上,多線程實現了一個混合的時間片和真正的併發,不同的線程同時在不同的CPU上執行代碼。還是存在某些時間片,因為操作系統需要服務它自己的線程,包括其他的應用的線程。
4.當一個線程的執行被內部因素打斷,比如時間片,則說這個線程是搶占式的。在大部分情形下,一個線程不能控制自己何時何地被搶占。
進程與線程
線程並行運行在一個單獨的進程中。進程之間是完全隔離的;線程在一定程度上隔離。運行在同一個應用程式下的線程共用堆記憶體。
進程間的切換:
先載入程式A的上下文,然後開始執行A,保存程式A的上下文,調入下一個要執行的程式B的程式上下文,然後開始執行B,保存程式B的上下文。。。
線程間的切換:
先載入程式A的上下文,然後執行A的A1線程,再執行A的A2線程。。。保存程式A的上下文;
1.線程結束無法重啟
class Program
{
static void Main(string[] args)
{
Thread thread = new Thread(threadOne);
thread.Start();
Thread.Sleep(5000);
//thread.Start();打開報錯
}
static void threadOne()
{
for(int i=0; i<100; i++)
{
lib.put("t"+i);
Thread.Sleep(10);
}
}
}
一旦開始,一個線程的IsAlive屬性返回true,直到這個線程結束。當傳遞給線程的構造函數的委托完成執行時,這個線程結束。一旦結束,這個線程不能重啟。
2.記憶體隔離
class Program { static void Main(string[] args) { Thread thread = new Thread(threadOne); thread.Start(); threadOne();//主線程調用 Thread.Sleep(5000); //thread.Start();打開報錯 } static void threadOne() { for(int i=0; i<10; i++) { lib.put("t"); Thread.Sleep(10); } } }
CLR給每個線程分配自己記憶體棧,因此局部變數可以保持分離。所以兩次調用threadOne都正常列印了10個"t"。
3. 數據共用
class Program { static bool bDone = false; static void Main(string[] args) { Thread thread = new Thread(threadOne); thread.Start(); threadOne(); Console.ReadKey(); } static void threadOne() { if(!bDone) { bDone = true; lib.put("threadOne"); } } }
如果多個線程對同一個對象實例有相同的引用,這些線程就共用這個對象實例的數據。所以只會輸出一個threadOne。
4.線程安全
上述例子,是有可能列印出兩次threadOne的。當bDone被置成true之前,若主線程和子線程都已經通過if判定,將列印兩次。
static void threadOne() { if(!bDone) { Thread.Sleep(50);//加上sleep後,會出現主線程和子線程都通過if判定後才把bDone置成true,所以列印兩次threadOne,
bDone = true;
lib.put("threadOne");
}
}
改進方式當讀\寫一個公共欄位時,獲取一個獨占鎖(exclusive lock)。C#提供了關鍵字lock。
class Program { static bool bDone = false; static readonly object locker = new object(); static void Main(string[] args) { Thread thread = new Thread(threadOne); thread.Start(); threadOne(); Console.ReadKey(); } static void threadOne() { lock (locker) { if (!bDone) { Thread.Sleep(50); bDone = true; lib.put("threadOne"); } } } }
當兩個線程同時搶占一個鎖時(在這個例子中,locker),一個線程等待,或者阻塞,直到這個鎖釋放。在這個例子中,這個鎖保證一次只有一個線程可以進入代碼的臨界區域,然後“threadOne”只會被列印一次。代碼在這種不確定的多線程背景下中被保護被叫做線程安全。
lock關鍵字將語句塊標記為關鍵部分,方法是獲取給定對象的互斥鎖,執行語句,然後釋放該鎖。在塊的開始處調用 Enter,而在塊的結尾處調用 Exit。這樣可確保當一個線程位於代碼的關鍵部分時,另一個線程不會進入該關鍵部分。 如果其他線程嘗試進入鎖定的代碼,則它將一直等待(即被阻止),直到該對象被釋放。一個線程,當阻塞的時候,不占用CPU資源。
5.Join和Sleep
Join是一種同步方法,阻止調用線程 (即,調用的方法的線程),直到線程其Join方法調用已完成。 使用此方法以確保線程已終止。 如果線程不會終止,調用方將無限期阻止。
Join有多個重載方法,可以在Join方法中添加一個參數,milliseconds或者timeSpan。如果這個線程結束了則Join方法返回true,如果這個線程超時則返回false,但不結束該線程。
static void Main(string[] args) { Thread thread = new Thread(threadOne); thread.Start(); thread.Join();//等到thread線程結束後繼續本線程 lib.print("end"); Console.ReadKey(); } static void threadOne() { lib.print("threadOne"); Thread.Sleep(3000); }
Sleep暫停當前線程一段指定的時間。
Thread.Sleep(TimeSpan.FromHours(1));//sleep一個小時
Thread.Sleep(500);//500 線程被阻塞的毫秒數。 指定零0以指示應掛起此線程以使其他等待線程能夠執行。
當使用Sleep或Join暫停線程時,這個線程是阻塞的,不消耗CPU資源。
--------------原文連接--------------