源:C++ Builder多線程編程技術經驗談html
線程之可行性編程
在不少狀況下,可能須要爲程序建立線程。這裏給出其中一些可能性: 安全
(1)若是建立的是一個多文檔接口(Multiple Document Interface,MDI)程序,那麼爲每一個窗口分配一個線程就顯得十分重要了,例如,對於一個經過多個Modem同時鏈接到多個主機的MDI通訊程序而言,若是每一個窗口都有它本身的線程來和一個主機通訊,那麼整個事情就簡化不少。 多線程
(2)若是使用的是一臺有多個處理器的機器,並但願充分利用全部可能得到的CPU資源,那麼就須要將應用程序分解成多個線程。Windows2000中CPU的劃分單位爲線程。所以,若是程序只包含一個線程,那麼,默認環境下該程序只能使用其中一個CPU.可是,若是將此程序劃分爲多個線程,那麼Windows2000就能夠在不一樣的CPU上運行各個線程。 併發
(3)在後臺運行的某些任務的同時,要求用戶還能夠繼續使用應用程序進行工做。利用線程很容易實現這點。例如:能夠將一些冗長的重算、頁面格式化操做、文件的讀寫等活動都放在單獨的線程中,使其在後臺運行,而不會對用戶形成影響。 異步
同步 函數
撰寫多線程程序的一個最具挑戰性的問題就是:如何讓一個線程和另外一個線程合做。這引出了一個很是重要的問題:同步。所謂同步是指進程、線程間相互通訊時避免破壞各自數據的能力。Windows環境下的同步問題是由Win32系統的CPU時間片分配方式引發的。雖然在某一時刻,只有一個線程佔用CPU(單CPU)時間,可是沒法知道在何時,在什麼地方線程被打斷,這樣如何保證線程之間不破壞彼此的數據就顯得格外重要。同步問題是如此重要,也至關有趣,於是吸引了很多學者對他進行研究,由此產成了一系列經典的進程同步問題,其中較有表明性的是"生產者-消費者問題"、"讀者-寫者問題""哲學家進餐問題"等。在此,本文簡要討論了C++Builder平臺下如何利用多線程編程技術實現"生產者-消費者"問題,幫助咱們更好得理解同步概念及其實現方法。 ui
生產者-消費者問題 spa
生產者-消費者問題是一個著名的進程同步問題。它描述的是:有一羣生產者進程在生產消息,並將此消息提供給消費者進程去消費。爲使生產者進程和消費者進程能併發進行,在他們之間設置了一個具備N個緩衝區的緩衝池,生產者進程能夠將它所生產的消息放入一個緩衝區中,消費者進程能夠從一個緩衝區中取得一個消息消費。儘管全部的生產者進程和消費者進程都是以異步方式進行的,但他們之間必須保持同步,即不容許消費者進程到一個空的緩衝區中去取消息,也不容許生產者進程向一個已裝滿消息且還沒有被取走消息的緩衝區中投放消息。 線程
C++Builder多線程應用程序編程基礎
一、使用C++Builder提供的TThread類
VCL類庫提供了用於線程編程的TThread類。在TThread類中封裝了Windows中關於線程機制的WindowSAPI.對於大多數的應用程序來講,可在應用程序中使用線程對象來表示執行線程。線程對象經過封裝使用線程所需的內容,簡化了多線程應用程序的編寫。注意,線程對象不容許控制線程堆棧的大小或其安全屬性。若須要控制這些,必須使用WindowsAPI的Create Thread()或Begin Thread()函數。
TThread類有如下一些屬性和方法:
1) 屬性:
·Priority:優先級屬性。能夠設置線程的優先級。
·Return Value:返回值屬性。當線程介紹時返回給其餘線程一個數值。
·Suspended:掛起屬性。能夠判斷線程是否被掛起。
·Terminated:結束屬性。用來標誌是否應該結束線程。
·ThreadID:標識號屬性。在整個系統中線程的標識號。使用Windows API函數時該屬性很是有用。
2) 方法:
·Do Terminate:產生一個On Terminate事件,可是不結束線程的執行。
·Resume:喚醒一個線程繼續執行。
·Suspend:掛起一個線程,要與Resume過程成對使用。
·Synchronize:由主VCL線程調用的一個同步過程。
·Terminate:將Terminate屬性設置爲True,停止線程的執行。
·Wait For:等待線程的停止並返回Return Value屬性的數值。
二、協調線程
在編寫線程執行時運行的代碼時,必須考慮到可能同步執行的其餘線程的行爲。特別注意,避免兩個線程試圖同時使用相同的全局對象或變量。另外,一個線程中的代碼會依賴其餘線程執行任務的結果。
(1) 避免同時訪問
爲避免在訪問全局對象或變量時與其餘線程發生衝突,可能須要暫停其餘線程的執行,直到該線程代碼完成操做。
(1)鎖定對象。一些對象內置了鎖定功能,以防止其餘線程使用該對象的實例。例如,畫布對象(TCanvas及其派生類)有一種Lock()函數能夠防止其餘線程訪問畫布,直到調用Unlock()函數。顯然,這種方法只對部分類有效。
(2)使用重要區段。
若對象沒有提供內置的鎖定功能,可以使用重要區段。重要區段像門同樣,每次只容許一個線程進入,要使用重要區段,需建立TCriticalSection的全局實例。TCriticalSection有兩個函數:Acquire()(阻止其餘線程執行該區域)及Release()(取消對其餘線程的阻止)。
(3)使用多重讀、獨佔寫的同步器。
當使用重要區段來保護全局內存時,每次只有一個線程可使用該內存。這種保護可能會超出了須要,特別是有一個常常讀但不多寫的對象或變量時更是如此。多個線程同時讀相同內存但沒有線程寫內存是沒有危險的。當有一些常常被讀,可是不多寫的全局變量時,可用TMultiReadExclusiveWriteSynchronizer對象保護它。這個對象和重要區段同樣,但它容許多個線程同時讀,只要沒有線程寫便可。每一個須要讀內存的線程首先要調用Begin Read()函數(確保當前無其餘線程寫內存),線程完成對保護內存讀操做後,要調用End Read()函數。任何線程須要寫保護內存必須調用Begin Write()函數(確保當前無其餘線程讀或寫內存),完成對保護內存寫操做後,調用End Write()函數。
(4)使用Synchronize函數:Void __fast call Synchronize (TThreadMethod &Method);
其中參數Method爲一個不帶參數的過程名。在這個不帶參數的過程當中是一些訪問VCL的代碼。咱們能夠在Execute過程當中調用Synchronize過程來避免對VCL的併發訪問。程序運行期間的具體過程其實是由Synchronize過程來通知主線程,而後主線程在適當的時機來執行Synchronize過程的參數列表中的那個不帶參數的過程。在多個線程的狀況下,主線程將Synchronize過程發過來的通知放到消息隊列中,而後逐個地響應這些消息。經過這種機制Synchronize實現了線程之間地同步。
3 等待其餘線程
若線程必須等待另外一線程完成某項任務,可以讓線程臨時中斷執行。而後,要麼等待另外一線程徹底執行結束,要麼等待另外一線程通知完成了該任務。
(1)等待線程執行結束
要等待另外一線程執行結束,使用它地Wait For()函數。Wait For函數直到那個線程終止才返回,終止的方式要麼完成了其Execute()函數,要麼因爲一個異常。
(2)等待任務完成。
有時,只須要等待線程完成一些操做而不是等待線程執行結束。爲此,可以使用一個事件對象。事件對象(TEvent)應具備全局範圍以便他們可以爲全部線程可見。當一個線程完成一個被其餘線程依賴的操做時,調用TEvent::Set Event()函數。Set Event發出一個信號,以便其餘線程能夠檢查並得知操做完成。要關掉信號,則使用Reset Event()函數。
例如,當必須等待若干線程完成其執行而不是單個線程時。由於不知道哪一個線程最後完成,也就不能對某個線程使用Wait For()函數。此時,可經過調用Set Event以在線程結束時累加計數值並在最後一個線程結束時發出信號以指示全部線程結束。
多線程應用程序編程實例
下面是一個實現"生產者-消費者問題"的多線程應用實例。在此例中,咱們按上面介紹的方法構造了兩個TThread的子類TProducerThread(生產者線程)和TCustomerThread(消費者線程),生產和消費的商品僅僅是一個整數。在協調生產和消費的過程當中,重要區段(TCriticalSection)和事件(TEvent)獲得了應用。生產者經過TEvent類的對象Begin Consume來通知消費者開始消費,而消費者經過TEent類的對象Begin Produce通知生產者開始生產。程序中共有兩個生產者,一個消費者。在兩個生產者之間,經過TCriticalSection類的對象同步。其運行界面如圖1所示。
圖1 程序運行效果
主要源程序以下所示:
生產者線程:
Void __fast call TProducerThread:: Execute () { //---- Place thread code here ---- Int i = 0; Int j; while(i<100) //每一個生產者線程生產100個商品 { Sleep(1000);//延遲,爲清楚得顯示執行效果 if(Form1->buffer_size > 0)//緩衝池不空,通知消費者消費 { Form1->Begin Consumer->Set Event (); } Form1->Produce Guard->Acquire (); i++; StrResult = IntToStr (i); J = Form1->buffer_size; Form1->Product [j] = i; Form1->buffer_size++; Synchronize(Show Result);//刷新界面,顯示最新生產-消費情況 Form1->Begin Consumer->Set Event();//通知消費者消費 if(Form1->buffer_size == 5)//緩衝池滿,掛起生產者線程,直到通知再生產 { Form1->Begin Produce->Wait For (INFINITE); } Sleep (1000); Form1->Produce Guard->Release (); } While (Form1->buffer_size > 0) { Form1->Begin Consumer->Set Event (); } }
消費者線程:
Void __fast call TConsumerThread::Execute() { //---- Place thread code here ---- Int j; For (int i = 0;i < 200;i++) { Sleep(100); //延遲,爲清楚得顯示執行效果 Form1->Begin Consumer->Wait For(INFINITE);//掛起消費者線程,直到通知再消費 J = Form1->buffer_size - 1; StrResult = IntToStr (Form1->Product [j]); Form1->buffer_size--; Synchronize(Show Result); //刷新界面,顯示最新生產-消費情況 if(Form1->buffer_size == 4)//緩衝池再也不full,喚醒因爲緩衝池full而掛起的生產者線程 { Form1->Begin Produce->Set Event (); } Sleep (100); } }
結論 本文討論了多線程編程及其可行性,說明了在Windows環境下進行多線程編程的意義,並重點討論了C++Builder平臺下如何開發多線程應用程序這一問題,經過實現"生產者-消費者問題"這一著名的進程同步問題,比較清晰地反映了在Windows環境下進行多線程編程技術及其實現的做用和效果。