• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            C++ Programmer's Cookbook

            {C++ 基礎} {C++ 高級} {C#界面,C++核心算法} {設計模式} {C#基礎}

            使用線程

            使用線程

            Greg Ewing
            Clarity Consulting Inc.


            2002 年 3 月

             

            摘要:本文論述了各種模式的線程(單線程、單元線程和自由線程)以及每種模式的使用方法。同時,還提供了一個使用線程的 C# 語言代碼示例,以幫助您編寫使用線程的應用程序。本文還討論了多線程代碼中的一些重要問題。

            下載(英文)示例文件。(請注意,在示例文件中,程序員的注釋使用的是英文,本文中將其譯為中文是為了便于讀者進行理解。)

            目錄

            簡介
            線程背景
            示例應用程序
            多線程代碼問題
            總結

            簡介

            編寫多線程 Microsoft® 消息隊列 (MSMQ) 觸發器應用程序向來是一件讓人畏懼的事情。不過,.NET 框架線程和消息類的出現使這項工作變得比以前容易了。這些類允許您使用任何適用于 .NET 框架的語言來編寫多線程應用程序。以前,像 Microsoft Visual Basic® 之類的工具對線程的支持十分有限。因此不得不使用 C++ 來編寫多線程代碼,通過 Visual Basic 構建由多個過程或 ActiveX DLL 組成的解決方案(這種解決方案一點也不理想),或者干脆完全放棄多線程。使用 .NET 框架,您可以構建各種多線程應用程序,而不用考慮選擇使用哪種語言。

            本文將逐步介紹構建偵聽并處理來自 Microsoft 消息隊列的多線程應用程序的過程。本文將著重討論兩個名稱空間 System.ThreadingSystem.Messaging。示例代碼是用 C# 語言編寫的,但您可以輕松地將其轉換為您所使用的語言。

            線程背景

            在 Win32 環境中,線程有三種基本模式:單線程、單元線程和自由線程。

            單線程

            您最初編寫的某些應用程序很可能是單線程應用程序,僅包含與應用程序進程對應的線程。進程可以被定義為應用程序的實例,擁有該應用程序的內存空間。大多數 Windows 應用程序都是單線程的,即用一個線程完成所有工作。

            單元線程

            單元線程是一種稍微復雜的線程模式。標記用于單元線程的代碼可以在其自己的線程中執行,并限制在自己的單元中。線程可以被定義為進程所擁有的實體。處理時將調度該進程。在單元線程模式中,所有線程都在主應用程序內存中各自的子段范圍內運行。此模式允許多個代碼實例同時但獨立地運行。例如,在 .NET 之前,Visual Basic 僅限于創建單元線程組件和應用程序。

            自由線程

            自由線程是最復雜的線程模式。在自由線程模式中,多個線程可以同時調用相同的方法和組件。與單元線程不同,自由線程不會被限制在獨立的內存空間。當應用程序必須進行大量相似而又獨立的數學計算時,您可能需要使用自由線程。在這種情況下,您需要生成多個線程使用相同的代碼示例來執行計算。可能 C++ 開發人員是僅有的編寫過自由線程應用程序的應用程序開發人員,因為像 Visual Basic 6.0 這樣的語言幾乎不可能編寫自由線程應用程序。

            使用線程模式

            為了使您對線程模式有一定的概念,我們可以將其想象為從一所屋子搬到另一所屋子。如果您采用單線程方法,則需要您自己完成從打包到扛箱子再到拆包的所有工作。如果您使用單元線程模式,則表示您邀請了好朋友來幫忙。每個朋友在一個單獨的房間里工作,并且不能幫助在其他房間工作的人。他們各自負責自己的空間和空間內的物品搬運。如果您采用自由線程方法,您仍然邀請相同的朋友來幫忙,但是所有朋友可以隨時在任何一個房間工作,共同打包物品。與此類似,您的房子就是運行所有線程的進程,每個朋友都是一個代碼實例,搬運的物品為應用程序的資源和變量。

            本示例解釋了不同線程模式的優點和缺點。單元線程比單線程要快,因為有多個組件實例在工作。在某些情況下,自由線程比單元線程更快更有效,這是因為所有事情同時發生,并且可以共享所有資源。但是,當多線程更改共享資源時,這可能會出現問題。假設一個人開始使用箱子打包廚房用具,此時另一個朋友進來了,要使用同一個箱子打包浴室的東西。第一個朋友在箱子上貼上了“廚房用具”,另一個朋友用“洗漱用品”標簽覆蓋了原標簽。結果,當您拆包時,就會發生將廚房用品搬到浴室的情況。

            示例應用程序

            第一步是要檢查示例應用程序的設計。應用程序將生成多個線程,每個線程都偵聽來自 MSMQ 隊列的消息。本示例使用兩個類,主 Form 類和自定義 MQListen 類。Form 類將處理用戶界面并創建,管理和破壞輔助線程。MQListen 類包含所有代碼,包括輔助線程運行所需的消息隊列因素。

            準備應用程序

            • 要啟動應用程序,請打開 Visual Studio .NET 并創建一個名為 MultiThreadedMQListener 的新 C# Windows 應用程序。打開窗體的屬性,將其命名為 QueueListenerForm。畫出初始窗體后,將兩個標簽、兩個按鈕、一個狀態欄和兩個文本框拖放到窗體上。將第一個文本框命名為 Server,第二個文本框命名為 Queue。將第一個按鈕命名為 StartListening,第二個按鈕命名為 StopListening。可以保留狀態欄的默認名稱 statusBar1
            • 下一步,單擊 Project(項目)菜單并單擊 Add Reference(添加引用),以向 System.Messaging 名稱空間添加一個引用。在 .NET 組件列表中找到并選擇 System.Messaging.Dll。名稱空間包含與 MSMQ 隊列通信所使用的類。
            • 下一步,單擊 File(文件)菜單,然后單擊 Add New Item(添加新項),以在項目中添加一個新類。選擇 Class(類)模板并將其命名為 MQListen。在類的頂部添加下列 using 語句:
              // C#
                  using System.Threading;
                  using System.Messaging;

              System.Threading 名稱空間允許您訪問所有必要的線程功能,在本例中,您可以訪問 Thread 類和 ThreadInterruptException 構造函數。該名稱空間還包括許多其他高級功能,本文不作詳細討論。System.Messaging 名稱空間允許您訪問 MSMQ 功能,包括向隊列發送消息和接收隊列消息。在本例中,您將使用 MessageQueue 類來接收消息。還必須在主窗體代碼中添加 using System.Threading

            所有引用就位后,您就可以開始編寫代碼了。

            輔助線程

            首先需要構建封裝所有線程工作的 MQListen 類。將下列代碼插入 MQListen 中。

            // C#
            public class MQListen
            {
            private string m_MachineName;
            private string m_QueueName;
            // 構造函數接收必要的隊列信息。
            public MQListen(string MachineName, string QueueName)
            {
            m_MachineName = MachineName;
            m_QueueName = QueueName;
            }
            // 每個線程用來偵聽 MQ 消息的一種唯一方法
            public void Listen()
            {
            // 創建一個 MessageQueue 對象。
            System.Messaging.MessageQueue MQ  = new
            System.Messaging.MessageQueue();
            // 設置 MessageQueue 對象的路徑屬性。
            MQ.Path = m_MachineName + "\\private$\\" + m_QueueName;
            // 創建一個 Message 對象。
            System.Messaging.Message Message = new
            System.Messaging.Message();
            // 重復上述步驟,直到收到中斷。
            while (true)
            {
            try
            {
            // 休眠以在中斷發出時捕捉中斷。
            System.Threading.Thread.Sleep(100);
            // 將 Message 對象設置為與接收函數的結果相等。
            // 持續時間(天、小時、分鐘、秒)。
            Message = MQ.Receive(new TimeSpan(0, 0, 0, 1));
            // 顯示已接收消息的標簽
            System.Windows.Forms.MessageBox.Show(" Label: " + Message.Label);
            }
            catch (ThreadInterruptedException e)
            {
            // 從主線程捕捉 ThreadInterrupt 并退出。
            Console.WriteLine("Exiting Thread");
            Message.Dispose();
            MQ.Dispose();
            break;
            }
            catch (Exception GenericException)
            {
            // 捕捉接收過程中拋出的所有異常。
            Console.WriteLine(GenericException.Message);
            }
            }
            }
            }

            代碼討論

            MQListen 類包含一個不同于構造函數的函數。該函數封裝每個輔助線程要執行的所有工作。在主線程中,您向線程構造函數傳遞一個對此函數的引用,以便在啟動線程時執行該函數。

            Listen 所做的第一件事情是設置一個消息隊列對象。MessageQueue 構造函數通過三種實現進行重載。第一種實現使用兩個參數:一個字符串參數,指定偵聽隊列的位置;一個布爾值參數,指示是否為訪問隊列的第一個應用程序賦予獨占讀取隊列的權限。第二種實現只使用隊列路徑參數,第三種實現不使用參數。為了簡便起見,您可以使用第三種實現,在下一行分配路徑。

            如果您引用了隊列,則必須創建一個消息對象。消息構造函數也有三種實現方式。如果您想將消息寫入隊列,則可以使用前兩種實現。這兩種實現采用兩個對象:一個是位于消息正文中的對象;一個是定義如何將對象序列化到消息正文的 IMessageFormatter 對象。在本例中,您將從隊列中讀取數據,以初始化空的消息對象。

            初始化對象后,您需要輸入執行所有工作的主循環。然后,當主線程調用 Interrupt 終止這些線程時,則只有在線程處于等待、睡眠或連接狀態下才會被中斷。如果沒有處于上述三種狀態,則要等到下次進入這三種狀態中的一種時才會被中斷。要確保輔助線程進入等待、睡眠或連接狀態,請調用位于 System.Threading 名稱空間的 Sleep 方法。對于使用過 Windows API 睡眠函數的 C++ 和 Visual Basic 開發人員而言,Sleep 方法并不陌生。它只使用一個參數:線程處于睡眠狀態的毫秒數。如果您從未調用過 Sleep,輔助線程將永遠不會進入可以接收中斷請求的狀態,而會無限制地繼續下去,除非您手動關閉進程。

            MQ Receive 方法有兩種實現。第一種實現不使用參數,將一直等待接收消息。第二種實現(本例使用這種實現)使用 TimeSpan 對象指定一個超時值。TimeSpan 構造函數包含四個參數:日、小時、分鐘和秒。在本例中,Receive 方法在超時和返回前將等待一秒種。

            收到的消息將被分配給先前創建的消息對象,然后,便可以對其進行處理了。本例打開一個帶有標簽的消息框,并刪除了此消息。如果您想在實際使用中采用此代碼,則可以在此處放置任何消息處理代碼。

            當輔助線程收到 Interrupt 請求后,將發出一個 ThreadInterruptedException 異常。要捕捉此異常,請在 try-catch 塊中包含 SleepReceive 函數。您應當指定兩個捕獲:第一個用于捕獲中斷異常,第二個用于處理捕獲到的錯誤異常。捕獲到中斷異常時,請首先將其寫入線程正在退出的調試窗口。下一步,對隊列對象和消息對象調用 Dispose 方法,以保證所有內存都被清空并發送到內存回收器。最后,中斷 while 循環。

            函數退出 while 循環后,關聯的線程也將立即結束,代碼為 0。在調試窗口,您將看到一則消息,例如“The thread '<name>' (0x660) has exited with code 0 (0x0)”(線程 '<name>' (0x660) 已經退出,代碼為 0 (0x0))。現在,線程已經退出該環境,并已自動被破壞。主線程和輔助線程都不需要執行專門的清除操作。

            主窗體

            下一步是向窗體添加代碼以生成輔助線程并針對各輔助線程啟動 MQListen 類。首先,請向窗體添加下列函數:

            // C#
            private void StartThreads()
            {
            int LoopCounter; // 線程計數
            StopListeningFlag = false; // 跟蹤輔助線程是否應當
            // 終止的標志。
            // 將一個包含 5 個線程的數組聲明為輔助線程。
            Thread[] ThreadArray = new Thread[5];
            // 聲明包含輔助線程的所有代碼的類。
            MQListen objMQListen = new
            MQListen(this.ServerName.Text,this.QueueName.Text);
            for (LoopCounter = 0; LoopCounter < NUMBER_THREADS; LoopCounter++)
            {
            // 創建一個 Thread 對象。
            ThreadArray[LoopCounter] = new Thread(new
            ThreadStart(objMQListen.Listen));
            // 啟動線程將調用 ThreadStart 委托。
            ThreadArray[LoopCounter].Start();
            }
            statusBar1.Text = LoopCounter.ToString() + " listener threads started";
            while (!StopListeningFlag)
            {
            // 等待用戶按下停止按鈕。
            // 在等待過程中,讓系統處理其他事件。
            System.Windows.Forms.Application.DoEvents();
            }
            statusBar1.Text = "Stop request received, stopping threads";
            // 向每個線程發送一個中斷請求。
            for (LoopCounter = 0;LoopCounter < NUMBER_THREADS; LoopCounter++)
            {
            ThreadArray[LoopCounter].Interrupt();
            }
            statusBar1.Text = "All Threads have been stopped";
            }

            代碼討論

            要啟動此函數,請創建一個包含 5 個項目的線程數組。此數組將保持對所有線程的引用,以備將來使用。

            MQListen 類的構造函數使用兩個參數:包含消息隊列的計算機名以及要偵聽的隊列的名稱。構造函數使用文本框中的值來為這兩個參數賦值。

            要創建線程,您需要進入循環以初始化每個線程對象。Thread 構造函數要求您向其傳遞一個委托,該委托在調用線程的 Start 方法時指向要調用的函數。您希望線程開始使用 MQListen.Listen 函數,但該線程并不是一個委托。為了滿足線程構造函數的要求,您必須傳遞一個 ThreadStart 對象,該對象將創建一個給定函數名稱的委托。此時,請向 ThreadStart 對象傳遞一個對 MQListen.Listen 函數的引用。由于該數組元素已被初始化,請立即調用 Start 來開始線程。

            所有線程開始后,請用相應的消息來更新窗體中的狀態欄。隨著線程的運行和偵聽隊列,主線程將等待用戶請求應用程序停止偵聽。為此,主線程將進入一個 while 循環,直至您單擊 StopListening 按鈕更改 StopListeningFlag 的值。在此等待循環中,將允許應用程序使用 Forms.Application.DoEvents 方法處理其他需要處理的工作。對于熟悉 Visual Basic 的讀者來說,這一點與舊的 DoEvents 方法相同。對于熟悉 C++ 的讀者來說,這等于編寫一個 MSG 泵。

            當用戶單擊 StopListening 按鈕時,該循環將退出并進入線程關閉代碼。要關閉所有線程,代碼必須檢查線程數組,并向每個線程發送一個中斷信號。在此循環內部,請對數組中的每個線程調用 Interrupt 方法。調用此方法之前,MQListen 類中的代碼將繼續正常執行。因此,您可以對每個輔助線程調用 Interrupt,而不必考慮線程是否正在處理其他事件。完成后,線程類將處理所有線程的清除。最后,請在退出前更新主窗體中的狀態欄。

            現在,您需要在按鈕后添加代碼。請向 StartListening 按鈕的 Click 事件添加以下代碼:

            // C#
            statusBar1.Text = "Starting Threads";
            StartThreads();

            這將更新狀態欄并調用 StartThreads 方法。對于 StopListening 按鈕,您只需使用以下代碼將 StopListeningFlag 設置為 True

            // C#
            StopListeningFlag = true;

            最后一步是為 StopListeningFlag 添加窗體級的變量。請在窗體代碼的頂部添加以下行:

            // C#
            private bool StopListeningFlag = false;

            要測試應用程序,您可以下載 MQWrite,這是一個寫入消息隊列的示例應用程序。

            多線程代碼問題

            您已經完成了示例代碼,因此您已經具備編寫自己的多線程應用程序所需的工具。線程可以顯著提高某些應用程序的性能和可伸縮性。在功能增強的同時,您還必須了解線程有危險的一面。使用線程可能會破壞您的應用程序,這樣的情況確實存在。線程可能會阻止運行,造成無法預料的后果,甚至會導致應用程序停止運行。

            如果您有多個線程,請確保它們之間不存在互相等待以到達某一點或完成的情況。如果操作錯誤,可能會導致死鎖狀態,兩個線程都無法完成,因為它們都在相互等待。

            如果多線程要求訪問不能輕易共享的資源(如軟盤驅動器、串行端口或紅外線端口),您可能需要避免使用線程或需要使用一種更高級的線程工具(如 synclocks 或 mutexes)來管理并發性。如果兩個線程試圖同時訪問這些資源,其中一個線程將無法獲得資源,或者會導致數據損壞。

            使用線程的另一個常見問題是競爭狀態。如果一個線程正在將數據寫入文件,而另一個線程正在從該文件中讀取數據,您將無法知道哪個線程先完成。這種情況稱為競爭狀態,因為兩個線程都在競相到達文件末尾。如果讀取線程快于寫入線程,則將返回無法預料的結果。

            使用線程時,還應當考慮所有線程是否都能夠完全獨立地進行工作。如果確實需要來回傳遞數據,在數據相對簡單的情況下,只要小心操作即可。傳遞復雜對象時,來回移動這些對象的封送代價將十分可觀。這將導致操作系統管理的額外開銷并且會降低總體性能。

            另一個問題是將代碼轉交給其他開發人員的傳遞成本。雖然 .NET 確實使線程變得容易,但請注意,維護您代碼的下一位開發人員必須了解要使用的線程。盡管這不是避免使用線程的理由,但是它充分說明了應該提供足夠的代碼注釋。

            這些問題本身并不能打消您使用線程的熱情,但您在設計應用程序和決定是否使用線程時應該考慮到這些問題。遺憾的是,本文無法詳細論述某些避免這些問題的方法。如果您已決定使用線程但遇到了上述某些問題,請檢查 synclocks 或 mutexes 看是否能解決問題或引導您使用其他解決方案。

            總結

            有了上述信息,您就可以編寫使用線程的應用程序。不過,在編寫過程中,請記住上面提到的問題。如果使用得當,那么,與單線程相比,多線程應用程序將具有更好的性能和可伸縮性。但是,如果使用不當,使用線程會適得其反,并且會導致應用程序不穩定。



            -----------------------------------------------------------------------------------------------------------------------------------------------------------------

             

            線程

            線程是一個能獨立于程序的其他部分運行的作業。線程屬于一個過程,獲得自己的CPU時間片。基于WIN32的應用程序可以使用多個可執行的線程,稱為多線程。Windows 3.x不能提供一種機制天然地支持多線程應用程序,但是一些為Windows 3.x編寫應用程序的公司使用他們自己的線程安排。

            基于WIN32的應用軟件能在給定的過程中產生多個線程。依靠生成多個線程,應用程序能夠完成一些后臺操作,例如計算,這樣程序就能運行得更快。當線程運行時,用戶仍能繼續影響程序。正如前面談到的,當一個應用程序運行時,就產生了一個相應的過程。那么應用程序就能有一個單獨的線程等待鍵盤輸入或執行一個操作,例如脫機打印或計算電子表格中各項的總數。

            在網絡世界中,當你試圖調整你站點的服務器的性能時,就要運行線程。如果你使用的是IIS,你可以在服務器上設置對于每個處理器所能創建的線程的最大數目。這樣,就能在處理器間更均勻地分配工作,從而加速你的站點。

            線程模式

            現在,為了讓你知道線程是什么和在哪能使用他們,讓我們看一下使用線程時你可能要運行的應用程序:ActiveX組件。ActiveX組件是獨立于其他代碼運行,基于COM的代碼。這聽起來是不是很熟悉?當你使用ActiveX組件時,必須在操作系統中注冊。其中的一條注冊信息就是,這個ActiveX組件是否支持多個線程,如果支持怎樣支持。這就是線程模式。

            組件支持的基本線程模式有:單線程,單元線程,組合線程。下面幾個部分將談談每一種模式對組件來說意味著什么。

            單線程

            如果組件被標記(即注冊)為單線程組件,這就意味著所有可執行函數(稱作方法)都將在組件的一個共享線程中運行。這就類似于沒有生成獨立的可執行線程的應用程序。單線程組件的缺點是一次只能運行一個方法。如果多次調用組件,例如調用組件中的存儲方法,就會產生瓶頸,因為一次只能有一個調用。

            如果你正在創建或使用一個ActiveX組件,建議不要使用單線程組件。

            單元線程

            如果一個組件被標記為單元線程,那么每個可執行的方法都將在一個和組件相聯系的線程上運行。之所以成為單元線程是因為,每個新生成的組件實例都有一個相應的線程單元,每個正在運行的組件都有它自己的線程。單元線程組件要比單線程組件要好,因為多個組件可以在各自的單元中同時運行方法。

            自由線程

            一個自由線程組件是一個支持多線程單元的多線程組件。這意味著多個方法調用可同時運行,因為每個調用都有自己的運行線程。這能使你的組件運行快得多,但也有一些缺點。運行在同一單元中的單元組件可以在單元中直接調用其他組件的方法,這是一個非常快的操作。但是,自由線程組件必須從一個單元向另一個單元調用。為了實現這一操作,WIN32生成了一個代理,用來通過單元界線。這對于每個需要的功能調用來說就產生了系統開銷,從而減低了系統的速度。每一個訪問自由組件的調用都有一個相應的代理。既然代理調用比直接調用慢,那么自然會有性能方面的降低。

            關于自由線程組件另一個需要注意的是:他們不是真正自由的。如果你創建了一個自由線程組件。你仍必須確保組件中的線程完全同步。這不是一件容易的事。只是簡單地把你的組件標記為是自由線程的,并不能使你的組件支持多線程,你仍要去做使你的組件自由線程化的工作。如果你不做這個工作,你的共享數據可能被破壞。這里說明一下為什么:讓我們假定你有一個方法計算某個數然后把它寫到某個變量中。此方法被傳入一個初始值例如是4,在隨后的計算中這個變量的值增長為5。在方法結束時這個最后的值被寫入到變量中。如果一次只有一個計算過程的話,所有這些會工作得很好。然而,當數據正在被改變時,另一個線程試圖訪問它,那么重新得到的數據就有可能是錯誤的。下面的圖表說明了這一點。

            為了修正這一錯誤,開發者為對象提供了線程同步。線程同步是在正在運行你想保護的某一其他代碼時運行的代碼。操作系統并不先占這個代碼,直到獲得一個可以中斷的信號。如果你想了解更多的有關線程同步對象的詳細內容,你不應該閱讀Geek Speak column!我的意思是,“注意看一下本文后面列出的參考閱讀文獻”。

            圖二,共享數據被多線程訪問搞亂了

             

            組合線程

            讀到這,你也許會想既然每種形式的線程都有自己的優點和缺點,為什么不把不同的線程模式結合起來使用呢?組合線程模式也許符合你的要求。一個被標記為組合線程的組件既有單元線程組件的特性又有自由線程組件的特性。當一個組件被標記為組合線程時,這個組件將總是在和生成它的對象所在單元相同的單元中創建。如果組件是被一個標記為單線程的對象創建的,那么這個組件的行為將和一個單元線程組件一樣,并且它將在線程單元中創建。這就意味著,組件和創建它的對象之間的調用,不需要一個為通信提供的代理調用。

            如果新組件是被自由線程組件創建的,那么這個組件將表現得像一個自由線程組件,但是它將在同一單元中運行,因此新組件能夠直接訪問創建它的對象(既不需代理調用)。切記,如果你打算把你的組件標記為組合線程,你必須提供線程同步保護你的線程數據。

            線程

            線程是一個能獨立于程序的其他部分運行的作業。線程屬于一個過程,獲得自己的CPU時間片。基于WIN32的應用程序可以使用多個可執行的線程,稱為多線程。Windows 3.x不能提供一種機制天然地支持多線程應用程序,但是一些為Windows 3.x編寫應用程序的公司使用他們自己的線程安排。

            基于WIN32的應用軟件能在給定的過程中產生多個線程。依靠生成多個線程,應用程序能夠完成一些后臺操作,例如計算,這樣程序就能運行得更快。當線程運行時,用戶仍能繼續影響程序。正如前面談到的,當一個應用程序運行時,就產生了一個相應的過程。那么應用程序就能有一個單獨的線程等待鍵盤輸入或執行一個操作,例如脫機打印或計算電子表格中各項的總數。

            在網絡世界中,當你試圖調整你站點的服務器的性能時,就要運行線程。如果你使用的是IIS,你可以在服務器上設置對于每個處理器所能創建的線程的最大數目。這樣,就能在處理器間更均勻地分配工作,從而加速你的站點。

            線程模式

            現在,為了讓你知道線程是什么和在哪能使用他們,讓我們看一下使用線程時你可能要運行的應用程序:ActiveX組件。ActiveX組件是獨立于其他代碼運行,基于COM的代碼。這聽起來是不是很熟悉?當你使用ActiveX組件時,必須在操作系統中注冊。其中的一條注冊信息就是,這個ActiveX組件是否支持多個線程,如果支持怎樣支持。這就是線程模式。

            組件支持的基本線程模式有:單線程,單元線程,組合線程。下面幾個部分將談談每一種模式對組件來說意味著什么。

            單線程

            如果組件被標記(即注冊)為單線程組件,這就意味著所有可執行函數(稱作方法)都將在組件的一個共享線程中運行。這就類似于沒有生成獨立的可執行線程的應用程序。單線程組件的缺點是一次只能運行一個方法。如果多次調用組件,例如調用組件中的存儲方法,就會產生瓶頸,因為一次只能有一個調用。

            如果你正在創建或使用一個ActiveX組件,建議不要使用單線程組件。

            單元線程

            如果一個組件被標記為單元線程,那么每個可執行的方法都將在一個和組件相聯系的線程上運行。之所以成為單元線程是因為,每個新生成的組件實例都有一個相應的線程單元,每個正在運行的組件都有它自己的線程。單元線程組件要比單線程組件要好,因為多個組件可以在各自的單元中同時運行方法。

            自由線程

            一個自由線程組件是一個支持多線程單元的多線程組件。這意味著多個方法調用可同時運行,因為每個調用都有自己的運行線程。這能使你的組件運行快得多,但也有一些缺點。運行在同一單元中的單元組件可以在單元中直接調用其他組件的方法,這是一個非常快的操作。但是,自由線程組件必須從一個單元向另一個單元調用。為了實現這一操作,WIN32生成了一個代理,用來通過單元界線。這對于每個需要的功能調用來說就產生了系統開銷,從而減低了系統的速度。每一個訪問自由組件的調用都有一個相應的代理。既然代理調用比直接調用慢,那么自然會有性能方面的降低。

            關于自由線程組件另一個需要注意的是:他們不是真正自由的。如果你創建了一個自由線程組件。你仍必須確保組件中的線程完全同步。這不是一件容易的事。只是簡單地把你的組件標記為是自由線程的,并不能使你的組件支持多線程,你仍要去做使你的組件自由線程化的工作。如果你不做這個工作,你的共享數據可能被破壞。這里說明一下為什么:讓我們假定你有一個方法計算某個數然后把它寫到某個變量中。此方法被傳入一個初始值例如是4,在隨后的計算中這個變量的值增長為5。在方法結束時這個最后的值被寫入到變量中。如果一次只有一個計算過程的話,所有這些會工作得很好。然而,當數據正在被改變時,另一個線程試圖訪問它,那么重新得到的數據就有可能是錯誤的。下面的圖表說明了這一點。

            為了修正這一錯誤,開發者為對象提供了線程同步。線程同步是在正在運行你想保護的某一其他代碼時運行的代碼。操作系統并不先占這個代碼,直到獲得一個可以中斷的信號。如果你想了解更多的有關線程同步對象的詳細內容,你不應該閱讀Geek Speak column!我的意思是,“注意看一下本文后面列出的參考閱讀文獻”。

            圖二,共享數據被多線程訪問搞亂了

             

            組合線程

            讀到這,你也許會想既然每種形式的線程都有自己的優點和缺點,為什么不把不同的線程模式結合起來使用呢?組合線程模式也許符合你的要求。一個被標記為組合線程的組件既有單元線程組件的特性又有自由線程組件的特性。當一個組件被標記為組合線程時,這個組件將總是在和生成它的對象所在單元相同的單元中創建。如果組件是被一個標記為單線程的對象創建的,那么這個組件的行為將和一個單元線程組件一樣,并且它將在線程單元中創建。這就意味著,組件和創建它的對象之間的調用,不需要一個為通信提供的代理調用。

            如果新組件是被自由線程組件創建的,那么這個組件將表現得像一個自由線程組件,但是它將在同一單元中運行,因此新組件能夠直接訪問創建它的對象(既不需代理調用)。切記,如果你打算把你的組件標記為組合線程,你必須提供線程同步保護你的線程數據。

            posted on 2005-12-08 09:02 夢在天涯 閱讀(1900) 評論(0)  編輯 收藏 引用 所屬分類: C#/.NET

            公告

            EMail:itech001#126.com

            導航

            統計

            • 隨筆 - 461
            • 文章 - 4
            • 評論 - 746
            • 引用 - 0

            常用鏈接

            隨筆分類

            隨筆檔案

            收藏夾

            Blogs

            c#(csharp)

            C++(cpp)

            Enlish

            Forums(bbs)

            My self

            Often go

            Useful Webs

            Xml/Uml/html

            搜索

            •  

            積分與排名

            • 積分 - 1804603
            • 排名 - 5

            最新評論

            閱讀排行榜

            欧美国产成人久久精品| 国产精品岛国久久久久| 国内精品九九久久精品 | 99久久精品无码一区二区毛片 | 国产∨亚洲V天堂无码久久久| 精品一区二区久久| 久久人人爽人人爽人人爽| 精品久久久久久中文字幕| 国内精品伊人久久久久妇| 97精品国产91久久久久久| 色综合久久久久综合99| 青青国产成人久久91网| 久久天天躁狠狠躁夜夜不卡| 国产精品久久久99| 无码超乳爆乳中文字幕久久| 性高湖久久久久久久久AAAAA| 狠狠88综合久久久久综合网| 久久人人爽人人爽人人片AV高清| 99久久人人爽亚洲精品美女| 无码人妻久久一区二区三区免费丨| 欧美粉嫩小泬久久久久久久| 色综合合久久天天综合绕视看| 无码精品久久久天天影视| 久久精品aⅴ无码中文字字幕不卡 久久精品成人欧美大片 | 中文无码久久精品| 亚洲午夜无码AV毛片久久| 久久93精品国产91久久综合| 久久精品国产99国产精偷| 韩国免费A级毛片久久| 午夜久久久久久禁播电影| 777午夜精品久久av蜜臀| 久久久久久久免费视频| 综合久久精品色| 合区精品久久久中文字幕一区| 久久久久久A亚洲欧洲AV冫| 国产香蕉97碰碰久久人人| 国产免费久久精品99久久| 99久久免费只有精品国产| 久久精品一区二区影院 | AV无码久久久久不卡网站下载| 久久亚洲AV成人无码电影|