對於編程新手,多線程編程可謂一個不小的挑戰。但.NET框架已對其進行了相當程度的簡化。事實上,所有程序員花少量時間去鑽研它,就能快速掌握基礎知識。本章介紹多線程編程的基礎,並提供一些用於試驗的示範代碼。 創建線程 進行多線程編程時,首先必須創建Thread的一個實例。你的選擇並不多,因為只有一個構造函數: Visual Basic Public Sub New( ByVal start As ThreadStart ) C# public Thread( ThreadStart start ); C++ public: Thread(ThreadStart* start ); ThreadStart參數是作為線程起點使用的一個方法的委託。委託的簽名是一個無參數方法,返回空值。如下所示: Visual Basic Public Delegate Sub ThreadStart() C# public delegate void ThreadStart(); C++ public __gc __delegate void ThreadStart(); 啟動線程 剛開始學習線程時,必須注意創建Thread對象的一個實例並不會造成線程啟動。要啟動線程,需調用Thread類的Start()方法,如下所示: Visual Basic Public Sub Start() C# public void Start(); C++ public: void Start(); 讓線程休眠 開發線程時,有時不希望讓它繼續運行,或者想讓線程延遲一段時間,等待其他線程運行。為此,可設置一個延遲循環,比如一個「什麼都不做」的for循環。但那樣做會浪費CPU時間。相反,應臨時中止線程,或讓它休眠。 很容易就能做到這一點,只需添加以下靜態/共享的Sleep()方法: Visual Basic Overloads Public Shared Sub Sleep(Integer) C# public static void Sleep(int); C++ public: static void Sleep(Int32); 這一行代碼導致當前線程休眠。休眠時間既可像上例那樣,使用以毫秒為單位的整數值來指定,也可使用一個TimeSpan結構來指定。 Sleep()方法也可獲取兩個特殊值:Infinite表明永遠休眠;0表示放棄線程當前剩餘的CPU時間分片。 注意main函數也是線程。換言之,可用Sleep()讓任何應用程序休眠。 中止線程 偶爾也要讓一個線程在另一個線程中非正常地中止。為此可調用Abort()方法。通常,該方法會永久性地中止一個指定線程的執行: Visual Basic Overloads Public Sub Abort() C# public void Abort(); C++ public: void Abort(); 注意上面說的是「通常」。用Abort()方法中止一個線程時,實際會在線程中引發一個ThreadAbortException異常。它和其他異常一樣是可以捕捉的。但和其他大多數線程不同的是,除非調用中止線程的ResetAbort()方法,否則ThreadAbortException會在catch塊的末尾被再次引發。調用ResetAbort()方法可取消中止操作,防止由ThreadAbortException中止線程。 Visual Basic Public Shared Sub ResetAbort() C# public static void ResetAbort(); C++ public: static void ResetAbort(); 注意中止的線程不可重啟。試圖那樣做,會引發一個ThreadStateException異常。 聯接線程 如希望在線程結束時立即執行一些代碼,應該怎麼辦?更籠統地說,假如一個線程需要等待另一個線程結束才能繼續,應該怎麼辦?這時,需要用Join()方法來「聯接」線程。 Visual Basic Overloads Public Sub Join() C# public void Join(); C++ public: void Join(); 聯接線程時,可使用3個重載的Join()方法之一。上例展示的是第一個重載方法,它不獲取任何參數,並一直等待線程結束。第二個重載方法則獲取一個Integer/int/Int32參數,它要麼等待由該參數指定的毫秒數,要麼等待線程中止——選擇兩者中等待時間最短的那一個。第三個重載方法則獲取一個TimeSpan結構,工作方式與前一個重載版本相同。 中斷線程 有時,我們希望一個工作線程等待某個事件的發生。在這種情況下,有的人喜歡把線程放到一個緊致的循環中,在其中等待事件的發生。但同樣地,這會浪費大量CPU時間。更好的做法是讓工作線程休眠,事件發生時再喚醒它。為此,可將Sleep()和Interrupt()方法與ThreadInterruptedException組合使用。 Visual Basic Public Sub Interrupt() C# public void Interrupt(); C++ public: void Interrupt(); 基本思路是使用Sleep()方法讓工作線程休眠。需要的事件發生時,就用Interrupt()成員方法中斷工作線程的休眠。簡單地說,只是讓Interrupt()方法引發一個ThreadInterruptedException異常,而不是直接中止Sleep()方法。所以,你需要使用異常處理技術,將Sleep()方法放到一個try塊中。然後,讓工作線程在catch塊中繼續執行。 例如在工作線程中,你需要以下偽代碼: Try << 等待事件發生>;>; catch << 一個ThreadInterruptedException異常 >;>; <<繼續處理>;>; 然後,可讓其他線程來執行Interrupt()方法,使上述線程繼續。 掛起和恢復線程 如工作線程知道何時休眠,就適合使用中斷。但有時也需要讓一個線程臨時中止一個不同的線程,並在以後重新啟動它。 例如,某個工作線程可能正在進行一些密集型的數值計算。不巧的是,另一個線程需要盡快在屏幕上描繪一幅大圖形(你應該確保用戶界面總是具有最高的優先級)。 可採取三種方式解決這個問題。第一種方式是不進行任何特殊處理,讓多線程處理引擎緩慢顯示圖形。第二種方式是提高圖形顯示線程的優先級(或降低工作線程的優先級),為圖形顯示分配更多的CPU時間。最後一種方式就是用Suspend()方法來掛起工作線程。 Visual Basic Public Sub Suspend() C# public void Suspend(); C++ public: void Suspend(); 接著可描繪圖形,並在完成之後用Resume()方法恢復工作線程: Visual Basic Public Sub Resume() C# public void Resume(); C++ public: void Resume(); 後台和前台線程 可使用兩種類型的線程。第一種是前台線程,它會一直運行,直到正常退出或中途終止。第二種是後台進程,它在最後一個前台線程結束後終止。使用Thread類的構造函數創建線程時,默認創建的是前台進程。要把它變成後台進程,必須將其IsBackground屬性變成true。在線程運行期間,你可自由地切換IsBackground屬性值。 線程實踐 前面講了足夠多的理論知識,接著來實際運用一下線程。下例提供了三個版本(每種語言一個),它綜合使用了前面討論的所有方法。 Visual Basic .NET例子 C#例子 C++例子 該例剛開始可能讓人摸不著頭腦,三個版本都會生成和圖A相似的輸出。 圖A [img]http://www.zdnet.com.cn/i/developer/story/200303/39112543/image001.gif[/img] 使用線程時,你會發現它們雖然易於編程,但操縱起來並不是那麼容易。可能要花大量時間試驗才能讓線程同步,這正是在這個簡單例子中,我們要頻繁地調用Sleep()和Join()方法的原因