什麼是線程?
每個正在系統上運行的程序都是一個進程。每個進程包含一到多個線程。進程也可能是整個程序或者是部分程序的動態執行。線程是一組指令的集合,或者是程序的特殊段,它可以在程序裡獨立執行。也可以把它理解為代碼運行的上下文。所以線程基本上是輕量級的進程,它負責在單個程序裡執行多任務。通常由操作系統負責多個線程的調度和執行。
什麼是多線程?
多線程是為了使得多個線程並行的工作以完成多項任務,以提高系統的效率。線程是在同一時間需要完成多項任務的時候被實現的。
使用線程的好處有以下幾點:
·使用線程可以把占據長時間的程序中的任務放到後台去處理
·用戶界面可以更加吸引人,這樣比如用戶點擊了一個按鈕去觸發某些事件的處理,可以彈出一個進度條來顯示處理的進度
·程序的運行速度可能加快
·在一些等待的任務實現上如用戶輸入、文件讀寫和網絡收發數據等,線程就比較游泳了。在這種情況下我們可以釋放一些珍貴的資源如內存占用等等。
還有其他很多使用多線程的好處,這裡就不一一說明了。
一些線程模型的背景
我們可以重點討論一下在Win32環境中常用的一些模型。
·單線程模型
在這種線程模型中,一個進程中只能有一個線程,剩下的進程必須等待當前的線程執行完。這種模型的缺點在於系統完成一個很小的任務都必須占用很長的時間。
·塊線程模型(單線程多塊模型STA)
這種模型裡,一個程序裡可能會包含多個執行的線程。在這裡,每個線程被分為進程裡一個單獨的塊。每個進程可以含有多個塊,可以共享多個塊中的數據。程序規定了每個塊中線程的執行時間。所有的請求通過Windows消息隊列進行串行化,這樣保證了每個時刻只能訪問一個塊,因而只有一個單獨的進程可以在某一個時刻得到執行。這種模型比單線程模型的好處在於,可以響應同一時刻的多個用戶請求的任務而不只是單個用戶請求。但它的性能還不是很好,因為它使用了串行化的線程模型,任務是一個接一個得到執行的。
·多線程塊模型(自由線程塊模型)
多線程塊模型(MTA)在每個進程裡只有一個塊而不是多個塊。這單個塊控制著多個線程而不是單個線程。這裡不需要消息隊列,因為所有的線程都是相同的塊的一個部分,並且可以共享。這樣的程序比單線程模型和STA的執行速度都要塊,因為降低了系統的負載,因而可以優化來減少系統idle的時間。這些應用程序一般比較復雜,因為程序員必須提供線程同步以保證線程不會並發的請求相同的資源,因而導致競爭情況的發生。這裡有必要提供一個鎖機制。但是這樣也許會導致系統死鎖的發生。
多線程在.NET裡如何工作?
在本質上和結構來說,.NET是一個多線程的環境。有兩種主要的多線程方法是.NET所提倡的:使用ThreadStart來開始你自己的進程,直接的(使用ThreadPool.QueueUserWorkItem)或者間接的(比如Stream.BeginRead,或者調用BeginInvoke)使用ThreadPool類。一般來說,你可以"手動"為長時間運行的任務創建一個新的線程,另外對於短時間運行的任務尤其是經常需要開始的那些,進程池是一個非常好的選擇。進程池可以同時運行多個任務,還可以使用框架類。對於資源緊缺需要進行同步的情況來說,它可以限制某一時刻只允許一個線程訪問資源。這種情況可以視為給線程實現了鎖機制。線程的基類是System.Threading。所有線程通過CLI來進行管理。
·創建線程:
創建一個新的Thread對象的實例。Thread的構造函數接受一個參數:
·執行線程:
使用Threading命名空間裡的start方法來運行線程:
·組合線程:
經常會出現需要組合多個線程的情況,就是當某個線程需要其他線程的結束來完成自己的任務。假設DummyThread必須等待DummyPriorityThread來完成自己的任務,我們只需要這樣做:
·暫停線程:
使得線程暫停給定的秒
·中止線程:
如果需要中止線程可以使用如下的代碼:
·同步
經常我們會遇到需要在線程間進行同步的情況,下面的代碼給出了一些方法:
·使用Interlock
C#提供了一個特殊的類叫做interlocked,就是提供了鎖機制的實現,我們可以加入如下的代碼實現鎖機制:
·使用鎖
這是為了鎖定代碼關鍵區域以進行同步,鎖定代碼如下:
·使用Monitor
當有需要進行線程管理的時候我們可以使用:
其他也有一些方法進行管理,這裡就不一一提及了。
線程的缺點
線程自然也有缺點,以下列出了一些:
·如果有大量的線程,會影響性能,因為操作系統需要在他們之間切換;
·更多的線程需要更多的內存空間
·線程會給程序帶來更多的bug,因此要小心使用
·線程的中止需要考慮其對程序運行的影響
·通常塊模型數據是在多個線程間共享的,需要一個合適的鎖系統替換掉數據共享