我們在《C#夯實基礎之多線程一:初識多線程》一文中第二部分中指出,既然windows最終發展出了多線程模型,按理說,我們直接使用一個.NetFramework的線程類就可以直接擼代碼了,但在這之前,我們還需要認識一下線程的一些基本特性,它們的出現並不是多余的,而是為了解決一部分問題出現的,畢竟存在即合理,我們先說主線程、前台線程和后台線程:
一.主線程、前台線程與后台線程
相信前幾年,大家都用過迅雷,用來下載文件是非常方便的,更重要的是速度快。那么,它的速度非常之快,全速下載的時候明顯地拉慢了整個系統的響應時間,說明他占用了大量的系統資源。那它為什么這么快?知乎上的yskin用戶解釋說『一個下載任務進來,迅雷把文件平分成10份,然后開10個線程分別下載。這時主界面是一個單獨的線程,並不會因為下載文件而卡死。而且主線程可以控制下屬線程,比如某個線程下載緩慢甚至停止,主線程可以把它強行關掉並重啟另外一個線程。 』這么多線程同時工作,正常情況下,下載速度會有質的提升。
那么,問題來了,當我暫停這個下載任務時,后面10個線程會關掉,但界面線程關閉掉這10個線程的時候,我們並沒有察覺,它們在后台悄默聲地就關掉了。這10個線程就是我們說的后台線程。
現在,我們把迅雷軟件退出了,那么所有的下載任務都關掉了,下載任務背后的線程自然也會關掉,這個迅雷軟件運行的線程就是我們說的主線程,它是一個前台線程。
但當我們再在網頁中重新找到可下載的內容時,迅雷的資源嗅探又可以檢測到這些內容,交提示我們是否要下載。它不隨着前面迅雷軟件的退出而退出。
如果你覺得這個例子不好理解,《C#高級編程》中也有一個,當你使用word來編輯文檔時,它會實時提供一些拼寫檢查,當你需要打印文檔時,可以選擇后台打印,在打印機打印文檔的同時,你可以繼續編輯當前文檔。同時word文檔關閉時,這個打印任務可繼續執行,直到打印出來,但拼寫檢查任務不會再執行。
這就是主線程、前台線程與后台線程的一些類比。下面,我們來說結論。
當一個程序啟動時,就有一個進程被操作系統(OS)創建,與此同時一個線程也立刻運行,該線程通常叫做程序的主線程(Main Thread),因為它是程序開始時就執行的,如果你需要再創建線程,那么創建的線程就是這個主線程的子線程,它是前台線程。
新建的子線程可以是前台線程或者后台線程,前台線程必須全部執行完,即使主線程關閉掉,這時進程仍然存活。后台線程在未執行完成時,如果前台線程關掉,則后台線程也會停掉,且不拋出異常。也就是說,前台線程與后台線程唯一的區別是后台線程不會阻止進程終止。可以在任何時候將前台線程修改為后台線程。
二.C#中的前台線程與后台線程
在C#中,Thread類默認創建的是前台線程,通過線程池(后面會講到)創建的線程都是后台線程。
1.創建前台線程和后台線程
1 static void Main(string[] args) 2 { 3 //當前主線程是個前台線程,且不能修改為后台線程 4 Console.WriteLine(Thread.CurrentThread.IsBackground); 5 6 //Thread創建的線程是前台線程 7 Thread th = new Thread(delegate() { Console.WriteLine("start a new thread"); }); 8 Console.WriteLine(th.IsBackground); 9 10 //Task使用程序池創建線程,默認為后台線程 11 Task task = new Task(() => Console.WriteLine("start a new task")); 12 13 Console.Read(); 14 }
輸出結果:
2.修改前台線程為后台線程
1 static void Main(string[] args) 2 { 3 //修改前台線程為后台線程 4 Thread th = new Thread(delegate() { Console.WriteLine("start a new thread"); }); 5 Console.WriteLine(th.IsBackground); 6 th.Start(); 7 th.IsBackground = true; 8 Console.WriteLine(th.IsBackground); 9 10 Console.Read(); 11 }
輸出結果
在C#中,前台線程可以修改為后台線程,這是由HostProtectionAttribute屬性的SelfAffectingThreading字段決定的,如果可以變成后台線程,則值為true.
3.前台線程阻止進程的關閉
static void Main(string[] args) { //前台線程阻止了主線程的關閉 Thread th = new Thread(delegate() { Thread.Sleep(6000); Console.WriteLine("start a new thread"); }); th.Start(); Console.WriteLine("main thread end"); }
輸出結果:這里主線程馬上執行完成,並不馬上關閉,前台線程等待6秒再執行輸出
4.后台線程不阻止進程的關閉
1 static void Main(string[] args) 2 { 3 //后台線程不阻止主線程的關閉 4 Thread th = new Thread(delegate() 5 { 6 Thread.Sleep(6000); 7 Console.WriteLine("start a new thread"); 8 9 }); 10 th.IsBackground = true; 11 th.Start(); 12 13 Console.WriteLine("main thread end"); 14 }
結果:不等線程執行完成,主線程執行完畢后自動退出。
三.參考文檔
http://technet.microsoft.com/zh-cn/library/system.threading.thread.isbackground