使用線程 -- ZT Microsoft.com
發(fā)表時(shí)間:2024-06-18 來(lái)源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]使用線程 Greg EwingClarity Consulting Inc.2002 年 3 月摘要:本文論述了各種模式的線程(單線程、單元線程和自由線程)以及每種模式的使用方法。同時(shí),還提供了一個(gè)使用線程的 C# 語(yǔ)言代碼示例,以幫助您編寫(xiě)使用線程的應(yīng)用程序。本文還討論了多線程代碼中的一些重要問(wèn)...
使用線程
Greg Ewing
Clarity Consulting Inc.
2002 年 3 月
摘要:本文論述了各種模式的線程(單線程、單元線程和自由線程)以及每種模式的使用方法。同時(shí),還提供了一個(gè)使用線程的 C# 語(yǔ)言代碼示例,以幫助您編寫(xiě)使用線程的應(yīng)用程序。本文還討論了多線程代碼中的一些重要問(wèn)題。
下載(英文)示例文件。(請(qǐng)注意,在示例文件中,程序員的注釋使用的是英文,本文中將其譯為中文是為了便于讀者進(jìn)行理解。)
目錄
簡(jiǎn)介
線程背景
示例應(yīng)用程序
多線程代碼問(wèn)題
總結(jié)
簡(jiǎn)介
編寫(xiě)多線程 Microsoft® 消息隊(duì)列 (MSMQ) 觸發(fā)器應(yīng)用程序向來(lái)是一件讓人畏懼的事情。不過(guò),.NET 框架線程和消息類(lèi)的出現(xiàn)使這項(xiàng)工作變得比以前容易了。這些類(lèi)允許您使用任何適用于 .NET 框架的語(yǔ)言來(lái)編寫(xiě)多線程應(yīng)用程序。以前,像 Microsoft Visual Basic® 之類(lèi)的工具對(duì)線程的支持十分有限。因此不得不使用 C++ 來(lái)編寫(xiě)多線程代碼,通過(guò) Visual Basic 構(gòu)建由多個(gè)過(guò)程或 ActiveX DLL 組成的解決方案(這種解決方案一點(diǎn)也不理想),或者干脆完全放棄多線程。使用 .NET 框架,您可以構(gòu)建各種多線程應(yīng)用程序,而不用考慮選擇使用哪種語(yǔ)言。
本文將逐步介紹構(gòu)建偵聽(tīng)并處理來(lái)自 Microsoft 消息隊(duì)列的多線程應(yīng)用程序的過(guò)程。本文將著重討論兩個(gè)名稱(chēng)空間 System.Threading 和 System.Messaging。示例代碼是用 C# 語(yǔ)言編寫(xiě)的,但您可以輕松地將其轉(zhuǎn)換為您所使用的語(yǔ)言。
線程背景
在 Win32 環(huán)境中,線程有三種基本模式:?jiǎn)尉程、單元線程和自由線程。
單線程
您最初編寫(xiě)的某些應(yīng)用程序很可能是單線程應(yīng)用程序,僅包含與應(yīng)用程序進(jìn)程對(duì)應(yīng)的線程。進(jìn)程可以被定義為應(yīng)用程序的實(shí)例,擁有該應(yīng)用程序的內(nèi)存空間。大多數(shù) Windows 應(yīng)用程序都是單線程的,即用一個(gè)線程完成所有工作。
單元線程
單元線程是一種稍微復(fù)雜的線程模式。標(biāo)記用于單元線程的代碼可以在其自己的線程中執(zhí)行,并限制在自己的單元中。線程可以被定義為進(jìn)程所擁有的實(shí)體。處理時(shí)將調(diào)度該進(jìn)程。在單元線程模式中,所有線程都在主應(yīng)用程序內(nèi)存中各自的子段范圍內(nèi)運(yùn)行。此模式允許多個(gè)代碼實(shí)例同時(shí)但獨(dú)立地運(yùn)行。例如,在 .NET 之前,Visual Basic 僅限于創(chuàng)建單元線程組件和應(yīng)用程序。
自由線程
自由線程是最復(fù)雜的線程模式。在自由線程模式中,多個(gè)線程可以同時(shí)調(diào)用相同的方法和組件。與單元線程不同,自由線程不會(huì)被限制在獨(dú)立的內(nèi)存空間。當(dāng)應(yīng)用程序必須進(jìn)行大量相似而又獨(dú)立的數(shù)學(xué)計(jì)算時(shí),您可能需要使用自由線程。在這種情況下,您需要生成多個(gè)線程使用相同的代碼示例來(lái)執(zhí)行計(jì)算?赡 C++ 開(kāi)發(fā)人員是僅有的編寫(xiě)過(guò)自由線程應(yīng)用程序的應(yīng)用程序開(kāi)發(fā)人員,因?yàn)橄?Visual Basic 6.0 這樣的語(yǔ)言幾乎不可能編寫(xiě)自由線程應(yīng)用程序。
使用線程模式
為了使您對(duì)線程模式有一定的概念,我們可以將其想象為從一所屋子搬到另一所屋子。如果您采用單線程方法,則需要您自己完成從打包到扛箱子再到拆包的所有工作。如果您使用單元線程模式,則表示您邀請(qǐng)了好朋友來(lái)幫忙。每個(gè)朋友在一個(gè)單獨(dú)的房間里工作,并且不能幫助在其他房間工作的人。他們各自負(fù)責(zé)自己的空間和空間內(nèi)的物品搬運(yùn)。如果您采用自由線程方法,您仍然邀請(qǐng)相同的朋友來(lái)幫忙,但是所有朋友可以隨時(shí)在任何一個(gè)房間工作,共同打包物品。與此類(lèi)似,您的房子就是運(yùn)行所有線程的進(jìn)程,每個(gè)朋友都是一個(gè)代碼實(shí)例,搬運(yùn)的物品為應(yīng)用程序的資源和變量。
本示例解釋了不同線程模式的優(yōu)點(diǎn)和缺點(diǎn)。單元線程比單線程要快,因?yàn)橛卸鄠(gè)組件實(shí)例在工作。在某些情況下,自由線程比單元線程更快更有效,這是因?yàn)樗惺虑橥瑫r(shí)發(fā)生,并且可以共享所有資源。但是,當(dāng)多線程更改共享資源時(shí),這可能會(huì)出現(xiàn)問(wèn)題。假設(shè)一個(gè)人開(kāi)始使用箱子打包廚房用具,此時(shí)另一個(gè)朋友進(jìn)來(lái)了,要使用同一個(gè)箱子打包浴室的東西。第一個(gè)朋友在箱子上貼上了“廚房用具”,另一個(gè)朋友用“洗漱用品”標(biāo)簽覆蓋了原標(biāo)簽。結(jié)果,當(dāng)您拆包時(shí),就會(huì)發(fā)生將廚房用品搬到浴室的情況。
示例應(yīng)用程序
第一步是要檢查示例應(yīng)用程序的設(shè)計(jì)。應(yīng)用程序?qū)⑸啥鄠(gè)線程,每個(gè)線程都偵聽(tīng)來(lái)自 MSMQ 隊(duì)列的消息。本示例使用兩個(gè)類(lèi),主 Form 類(lèi)和自定義 MQListen 類(lèi)。Form 類(lèi)將處理用戶界面并創(chuàng)建,管理和破壞輔助線程。MQListen 類(lèi)包含所有代碼,包括輔助線程運(yùn)行所需的消息隊(duì)列因素。
準(zhǔn)備應(yīng)用程序
要啟動(dòng)應(yīng)用程序,請(qǐng)打開(kāi) Visual Studio .NET 并創(chuàng)建一個(gè)名為 MultiThreadedMQListener 的新 C# Windows 應(yīng)用程序。打開(kāi)窗體的屬性,將其命名為 QueueListenerForm。畫(huà)出初始窗體后,將兩個(gè)標(biāo)簽、兩個(gè)按鈕、一個(gè)狀態(tài)欄和兩個(gè)文本框拖放到窗體上。將第一個(gè)文本框命名為 Server,第二個(gè)文本框命名為 Queue。將第一個(gè)按鈕命名為 StartListening,第二個(gè)按鈕命名為 StopListening?梢员A魻顟B(tài)欄的默認(rèn)名稱(chēng) statusBar1。
下一步,單擊 Project(項(xiàng)目)菜單并單擊 Add Reference(添加引用),以向 System.Messaging 名稱(chēng)空間添加一個(gè)引用。在 .NET 組件列表中找到并選擇 System.Messaging.Dll。名稱(chēng)空間包含與 MSMQ 隊(duì)列通信所使用的類(lèi)。
下一步,單擊 File(文件)菜單,然后單擊 Add New Item(添加新項(xiàng)),以在項(xiàng)目中添加一個(gè)新類(lèi)。選擇 Class(類(lèi))模板并將其命名為 MQListen。在類(lèi)的頂部添加下列 using 語(yǔ)句:
// C#
using System.Threading;
using System.Messaging;
System.Threading 名稱(chēng)空間允許您訪問(wèn)所有必要的線程功能,在本例中,您可以訪問(wèn) Thread 類(lèi)和 ThreadInterruptException 構(gòu)造函數(shù)。該名稱(chēng)空間還包括許多其他高級(jí)功能,本文不作詳細(xì)討論。System.Messaging 名稱(chēng)空間允許您訪問(wèn) MSMQ 功能,包括向隊(duì)列發(fā)送消息和接收隊(duì)列消息。在本例中,您將使用 MessageQueue 類(lèi)來(lái)接收消息。還必須在主窗體代碼中添加 using System.Threading。
所有引用就位后,您就可以開(kāi)始編寫(xiě)代碼了。
輔助線程
首先需要構(gòu)建封裝所有線程工作的 MQListen 類(lèi)。將下列代碼插入 MQListen 中。
// C#
public class MQListen
{
private string m_MachineName;
private string m_QueueName;
// 構(gòu)造函數(shù)接收必要的隊(duì)列信息。
public MQListen(string MachineName, string QueueName)
{
m_MachineName = MachineName;
m_QueueName = QueueName;
}
// 每個(gè)線程用來(lái)偵聽(tīng) MQ 消息的一種唯一方法
public void Listen()
{
// 創(chuàng)建一個(gè) MessageQueue 對(duì)象。
System.Messaging.MessageQueue MQ= new
System.Messaging.MessageQueue();
// 設(shè)置 MessageQueue 對(duì)象的路徑屬性。
MQ.Path = m_MachineName + "\\private$\\" + m_QueueName;
// 創(chuàng)建一個(gè) Message 對(duì)象。
System.Messaging.Message Message = new
System.Messaging.Message();
// 重復(fù)上述步驟,直到收到中斷。
while (true)
{
try
{
// 休眠以在中斷發(fā)出時(shí)捕捉中斷。
System.Threading.Thread.Sleep(100);
// 將 Message 對(duì)象設(shè)置為與接收函數(shù)的結(jié)果相等。
// 持續(xù)時(shí)間(天、小時(shí)、分鐘、秒)。
Message = MQ.Receive(new TimeSpan(0, 0, 0, 1));
// 顯示已接收消息的標(biāo)簽
System.Windows.Forms.MessageBox.Show(" Label: " + Message.Label);
}
catch (ThreadInterruptedException e)
{
// 從主線程捕捉 ThreadInterrupt 并退出。
Console.WriteLine("Exiting Thread");
Message.Dispose();
MQ.Dispose();
break;
}
catch (Exception GenericException)
{
// 捕捉接收過(guò)程中拋出的所有異常。
Console.WriteLine(GenericException.Message);
}
}
}
}
代碼討論
MQListen 類(lèi)包含一個(gè)不同于構(gòu)造函數(shù)的函數(shù)。該函數(shù)封裝每個(gè)輔助線程要執(zhí)行的所有工作。在主線程中,您向線程構(gòu)造函數(shù)傳遞一個(gè)對(duì)此函數(shù)的引用,以便在啟動(dòng)線程時(shí)執(zhí)行該函數(shù)。
Listen 所做的第一件事情是設(shè)置一個(gè)消息隊(duì)列對(duì)象。MessageQueue 構(gòu)造函數(shù)通過(guò)三種實(shí)現(xiàn)進(jìn)行重載。第一種實(shí)現(xiàn)使用兩個(gè)參數(shù):一個(gè)字符串參數(shù),指定偵聽(tīng)隊(duì)列的位置;一個(gè)布爾值參數(shù),指示是否為訪問(wèn)隊(duì)列的第一個(gè)應(yīng)用程序賦予獨(dú)占讀取隊(duì)列的權(quán)限。第二種實(shí)現(xiàn)只使用隊(duì)列路徑參數(shù),第三種實(shí)現(xiàn)不使用參數(shù)。為了簡(jiǎn)便起見(jiàn),您可以使用第三種實(shí)現(xiàn),在下一行分配路徑。
如果您引用了隊(duì)列,則必須創(chuàng)建一個(gè)消息對(duì)象。消息構(gòu)造函數(shù)也有三種實(shí)現(xiàn)方式。如果您想將消息寫(xiě)入隊(duì)列,則可以使用前兩種實(shí)現(xiàn)。這兩種實(shí)現(xiàn)采用兩個(gè)對(duì)象:一個(gè)是位于消息正文中的對(duì)象;一個(gè)是定義如何將對(duì)象序列化到消息正文的 IMessageFormatter 對(duì)象。在本例中,您將從隊(duì)列中讀取數(shù)據(jù),以初始化空的消息對(duì)象。
初始化對(duì)象后,您需要輸入執(zhí)行所有工作的主循環(huán)。然后,當(dāng)主線程調(diào)用 Interrupt 終止這些線程時(shí),則只有在線程處于等待、睡眠或連接狀態(tài)下才會(huì)被中斷。如果沒(méi)有處于上述三種狀態(tài),則要等到下次進(jìn)入這三種狀態(tài)中的一種時(shí)才會(huì)被中斷。要確保輔助線程進(jìn)入等待、睡眠或連接狀態(tài),請(qǐng)調(diào)用位于 System.Threading 名稱(chēng)空間的 Sleep 方法。對(duì)于使用過(guò) Windows API 睡眠函數(shù)的 C++ 和 Visual Basic 開(kāi)發(fā)人員而言,Sleep 方法并不陌生。它只使用一個(gè)參數(shù):線程處于睡眠狀態(tài)的毫秒數(shù)。如果您從未調(diào)用過(guò) Sleep,輔助線程將永遠(yuǎn)不會(huì)進(jìn)入可以接收中斷請(qǐng)求的狀態(tài),而會(huì)無(wú)限制地繼續(xù)下去,除非您手動(dòng)關(guān)閉進(jìn)程。
MQ Receive 方法有兩種實(shí)現(xiàn)。第一種實(shí)現(xiàn)不使用參數(shù),將一直等待接收消息。第二種實(shí)現(xiàn)(本例使用這種實(shí)現(xiàn))使用 TimeSpan 對(duì)象指定一個(gè)超時(shí)值。TimeSpan 構(gòu)造函數(shù)包含四個(gè)參數(shù):日、小時(shí)、分鐘和秒。在本例中,Receive 方法在超時(shí)和返回前將等待一秒種。
收到的消息將被分配給先前創(chuàng)建的消息對(duì)象,然后,便可以對(duì)其進(jìn)行處理了。本例打開(kāi)一個(gè)帶有標(biāo)簽的消息框,并刪除了此消息。如果您想在實(shí)際使用中采用此代碼,則可以在此處放置任何消息處理代碼。
當(dāng)輔助線程收到 Interrupt 請(qǐng)求后,將發(fā)出一個(gè) ThreadInterruptedException 異常。要捕捉此異常,請(qǐng)?jiān)?try-catch 塊中包含 Sleep 和 Receive 函數(shù)。您應(yīng)當(dāng)指定兩個(gè)捕獲:第一個(gè)用于捕獲中斷異常,第二個(gè)用于處理捕獲到的錯(cuò)誤異常。捕獲到中斷異常時(shí),請(qǐng)首先將其寫(xiě)入線程正在退出的調(diào)試窗口。下一步,對(duì)隊(duì)列對(duì)象和消息對(duì)象調(diào)用 Dispose 方法,以保證所有內(nèi)存都被清空并發(fā)送到內(nèi)存回收器。最后,中斷 while 循環(huán)。
函數(shù)退出 while 循環(huán)后,關(guān)聯(lián)的線程也將立即結(jié)束,代碼為 0。在調(diào)試窗口,您將看到一則消息,例如“The thread '<name>' (0x660) has exited with code 0 (0x0)”(線程 '<name>' (0x660) 已經(jīng)退出,代碼為 0 (0x0)),F(xiàn)在,線程已經(jīng)退出該環(huán)境,并已自動(dòng)被破壞。主線程和輔助線程都不需要執(zhí)行專(zhuān)門(mén)的清除操作。
主窗體
下一步是向窗體添加代碼以生成輔助線程并針對(duì)各輔助線程啟動(dòng) MQListen 類(lèi)。首先,請(qǐng)向窗體添加下列函數(shù):
// C#
private void StartThreads()
{
int LoopCounter; // 線程計(jì)數(shù)
StopListeningFlag = false; // 跟蹤輔助線程是否應(yīng)當(dāng)
// 終止的標(biāo)志。
// 將一個(gè)包含 5 個(gè)線程的數(shù)組聲明為輔助線程。
Thread[] ThreadArray = new Thread[5];
// 聲明包含輔助線程的所有代碼的類(lèi)。
MQListen objMQListen = new
MQListen(this.ServerName.Text,this.QueueName.Text);
for (LoopCounter = 0; LoopCounter < NUMBER_THREADS; LoopCounter++)
{
// 創(chuàng)建一個(gè) Thread 對(duì)象。
ThreadArray[LoopCounter] = new Thread(new
ThreadStart(objMQListen.Listen));
// 啟動(dòng)線程將調(diào)用 ThreadStart 委托。
ThreadArray[LoopCounter].Start();
}
statusBar1.Text = LoopCounter.ToString() + " listener threads started";
while (!StopListeningFlag)
{
// 等待用戶按下停止按鈕。
// 在等待過(guò)程中,讓系統(tǒng)處理其他事件。
System.Windows.Forms.Application.DoEvents();
}
statusBar1.Text = "Stop request received, stopping threads";
// 向每個(gè)線程發(fā)送一個(gè)中斷請(qǐng)求。
for (LoopCounter = 0;LoopCounter < NUMBER_THREADS; LoopCounter++)
{
ThreadArray[LoopCounter].Interrupt();
}
statusBar1.Text = "All Threads have been stopped";
}
代碼討論
要啟動(dòng)此函數(shù),請(qǐng)創(chuàng)建一個(gè)包含 5 個(gè)項(xiàng)目的線程數(shù)組。此數(shù)組將保持對(duì)所有線程的引用,以備將來(lái)使用。
MQListen 類(lèi)的構(gòu)造函數(shù)使用兩個(gè)參數(shù):包含消息隊(duì)列的計(jì)算機(jī)名以及要偵聽(tīng)的隊(duì)列的名稱(chēng)。構(gòu)造函數(shù)使用文本框中的值來(lái)為這兩個(gè)參數(shù)賦值。
要?jiǎng)?chuàng)建線程,您需要進(jìn)入循環(huán)以初始化每個(gè)線程對(duì)象。Thread 構(gòu)造函數(shù)要求您向其傳遞一個(gè)委托,該委托在調(diào)用線程的 Start 方法時(shí)指向要調(diào)用的函數(shù)。您希望線程開(kāi)始使用 MQListen.Listen 函數(shù),但該線程并不是一個(gè)委托。為了滿足線程構(gòu)造函數(shù)的要求,您必須傳遞一個(gè) ThreadStart 對(duì)象,該對(duì)象將創(chuàng)建一個(gè)給定函數(shù)名稱(chēng)的委托。此時(shí),請(qǐng)向 ThreadStart 對(duì)象傳遞一個(gè)對(duì) MQListen.Listen 函數(shù)的引用。由于該數(shù)組元素已被初始化,請(qǐng)立即調(diào)用 Start 來(lái)開(kāi)始線程。
所有線程開(kāi)始后,請(qǐng)用相應(yīng)的消息來(lái)更新窗體中的狀態(tài)欄。隨著線程的運(yùn)行和偵聽(tīng)隊(duì)列,主線程將等待用戶請(qǐng)求應(yīng)用程序停止偵聽(tīng)。為此,主線程將進(jìn)入一個(gè) while 循環(huán),直至您單擊 StopListening 按鈕更改 StopListeningFlag 的值。在此等待循環(huán)中,將允許應(yīng)用程序使用 Forms.Application.DoEvents 方法處理其他需要處理的工作。對(duì)于熟悉 Visual Basic 的讀者來(lái)說(shuō),這一點(diǎn)與舊的 DoEvents 方法相同。對(duì)于熟悉 C++ 的讀者來(lái)說(shuō),這等于編寫(xiě)一個(gè) MSG 泵。
當(dāng)用戶單擊 StopListening 按鈕時(shí),該循環(huán)將退出并進(jìn)入線程關(guān)閉代碼。要關(guān)閉所有線程,代碼必須檢查線程數(shù)組,并向每個(gè)線程發(fā)送一個(gè)中斷信號(hào)。在此循環(huán)內(nèi)部,請(qǐng)對(duì)數(shù)組中的每個(gè)線程調(diào)用 Interrupt 方法。調(diào)用此方法之前,MQListen 類(lèi)中的代碼將繼續(xù)正常執(zhí)行。因此,您可以對(duì)每個(gè)輔助線程調(diào)用 Interrupt,而不必考慮線程是否正在處理其他事件。完成后,線程類(lèi)將處理所有線程的清除。最后,請(qǐng)?jiān)谕顺銮案轮鞔绑w中的狀態(tài)欄。
現(xiàn)在,您需要在按鈕后添加代碼。請(qǐng)向 StartListening 按鈕的 Click 事件添加以下代碼:
// C#
statusBar1.Text = "Starting Threads";
StartThreads();
這將更新?tīng)顟B(tài)欄并調(diào)用 StartThreads 方法。對(duì)于 StopListening 按鈕,您只需使用以下代碼將 StopListeningFlag 設(shè)置為 True:
// C#
StopListeningFlag = true;
最后一步是為 StopListeningFlag 添加窗體級(jí)的變量。請(qǐng)?jiān)诖绑w代碼的頂部添加以下行:
// C#
private bool StopListeningFlag = false;
要測(cè)試應(yīng)用程序,您可以下載 MQWrite,這是一個(gè)寫(xiě)入消息隊(duì)列的示例應(yīng)用程序。
多線程代碼問(wèn)題
您已經(jīng)完成了示例代碼,因此您已經(jīng)具備編寫(xiě)自己的多線程應(yīng)用程序所需的工具。線程可以顯著提高某些應(yīng)用程序的性能和可伸縮性。在功能增強(qiáng)的同時(shí),您還必須了解線程有危險(xiǎn)的一面。使用線程可能會(huì)破壞您的應(yīng)用程序,這樣的情況確實(shí)存在。線程可能會(huì)阻止運(yùn)行,造成無(wú)法預(yù)料的后果,甚至?xí)䦟?dǎo)致應(yīng)用程序停止運(yùn)行。
如果您有多個(gè)線程,請(qǐng)確保它們之間不存在互相等待以到達(dá)某一點(diǎn)或完成的情況。如果操作錯(cuò)誤,可能會(huì)導(dǎo)致死鎖狀態(tài),兩個(gè)線程都無(wú)法完成,因?yàn)樗鼈兌荚谙嗷サ却?br>
如果多線程要求訪問(wèn)不能輕易共享的資源(如軟盤(pán)驅(qū)動(dòng)器、串行端口或紅外線端口),您可能需要避免使用線程或需要使用一種更高級(jí)的線程工具(如 synclocks 或 mutexes)來(lái)管理并發(fā)性。如果兩個(gè)線程試圖同時(shí)訪問(wèn)這些資源,其中一個(gè)線程將無(wú)法獲得資源,或者會(huì)導(dǎo)致數(shù)據(jù)損壞。
使用線程的另一個(gè)常見(jiàn)問(wèn)題是競(jìng)爭(zhēng)狀態(tài)。如果一個(gè)線程正在將數(shù)據(jù)寫(xiě)入文件,而另一個(gè)線程正在從該文件中讀取數(shù)據(jù),您將無(wú)法知道哪個(gè)線程先完成。這種情況稱(chēng)為競(jìng)爭(zhēng)狀態(tài),因?yàn)閮蓚(gè)線程都在競(jìng)相到達(dá)文件末尾。如果讀取線程快于寫(xiě)入線程,則將返回?zé)o法預(yù)料的結(jié)果。
使用線程時(shí),還應(yīng)當(dāng)考慮所有線程是否都能夠完全獨(dú)立地進(jìn)行工作。如果確實(shí)需要來(lái)回傳遞數(shù)據(jù),在數(shù)據(jù)相對(duì)簡(jiǎn)單的情況下,只要小心操作即可。傳遞復(fù)雜對(duì)象時(shí),來(lái)回移動(dòng)這些對(duì)象的封送代價(jià)將十分可觀。這將導(dǎo)致操作系統(tǒng)管理的額外開(kāi)銷(xiāo)并且會(huì)降低總體性能。
另一個(gè)問(wèn)題是將代碼轉(zhuǎn)交給其他開(kāi)發(fā)人員的傳遞成本。雖然 .NET 確實(shí)使線程變得容易,但請(qǐng)注意,維護(hù)您代碼的下一位開(kāi)發(fā)人員必須了解要使用的線程。盡管這不是避免使用線程的理由,但是它充分說(shuō)明了應(yīng)該提供足夠的代碼注釋。
這些問(wèn)題本身并不能打消您使用線程的熱情,但您在設(shè)計(jì)應(yīng)用程序和決定是否使用線程時(shí)應(yīng)該考慮到這些問(wèn)題。遺憾的是,本文無(wú)法詳細(xì)論述某些避免這些問(wèn)題的方法。如果您已決定使用線程但遇到了上述某些問(wèn)題,請(qǐng)檢查 synclocks 或 mutexes 看是否能解決問(wèn)題或引導(dǎo)您使用其他解決方案。
總結(jié)
有了上述信息,您就可以編寫(xiě)使用線程的應(yīng)用程序。不過(guò),在編寫(xiě)過(guò)程中,請(qǐng)記住上面提到的問(wèn)題。如果使用得當(dāng),那么,與單線程相比,多線程應(yīng)用程序?qū)⒕哂懈玫男阅芎涂缮炜s性。但是,如果使用不當(dāng),使用線程會(huì)適得其反,并且會(huì)導(dǎo)致應(yīng)用程序不穩(wěn)定。