BCB 多线程的同步与协调
生活随笔
收集整理的這篇文章主要介紹了
BCB 多线程的同步与协调
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
多線程編程是提高系統(tǒng)資源利用率的一種常見(jiàn)方式。它占用的資源更小,啟動(dòng)更快,還可以實(shí)現(xiàn)在后臺(tái)運(yùn)行一些需時(shí)較長(zhǎng)的操作。[喝小酒的網(wǎng)摘]http://blog.hehehehehe.cn/a/8498.htm
一、初識(shí)TThread對(duì)象VCL提供了用于多線程編程的TThread類,在這個(gè)類中封裝了Windows關(guān)于線程機(jī)制的Windows API,通常將它的實(shí)例成為線程對(duì)象。線程對(duì)象通過(guò)封裝簡(jiǎn)化了多線程應(yīng)用程序的編寫(xiě)。注意,線程對(duì)象不允許控制線程堆棧的大小或安全屬性。若需要控制這 些,必須使用Windows API的CreateThread()或BeginThread()函數(shù)。不過(guò),即使是使用Windows Thread API函數(shù)建立和控制多線程,仍然可從一些同步線程對(duì)象或下節(jié)將要描述的方法中受益。要在應(yīng)用程序中使用線程對(duì)象,必須創(chuàng)建TThread的一個(gè)派生類。File|New|Thread Object,系統(tǒng)會(huì)提示為新線程對(duì)象提供類名,我們將其命名為T(mén)MyThread。我們必須自行在構(gòu)造函數(shù)以及Execute()函數(shù)中添加代碼。自動(dòng) 生成的構(gòu)造函數(shù)中有一個(gè)參數(shù),如果為true的話線程創(chuàng)建后將進(jìn)入掛起狀態(tài),直到線程對(duì)象的Resume()函數(shù)被調(diào)用才開(kāi)始執(zhí)行。如果為false則線 程創(chuàng)建后會(huì)立刻開(kāi)始執(zhí)行。我們先創(chuàng)建一個(gè)實(shí)例來(lái)親自感受下多線程:在窗體上放兩個(gè)Button和兩個(gè)Edit組件,自動(dòng)命名。然后File|New|Thread Object來(lái)創(chuàng)建一個(gè)線程對(duì)象,命名為T(mén)MyThread。以下請(qǐng)看完整工程代碼:
//Unit1.h //主窗體頭文件//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include "Unit2.h"
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TButton *Button1;
TButton *Button2;
TEdit *Edit1;
TEdit *Edit2;
void __fastcall Button1Click(TObject *Sender);
void __fastcall Button2Click(TObject *Sender);
void __fastcall FormCreate(TObject *Sender);
private: // User declarations
TMyThread *thread1,*thread2;
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif//Unit1.cpp //主窗體實(shí)現(xiàn)文件//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
thread1->Resume(); //單擊后才啟動(dòng)線程
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
thread2->Resume();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
thread1=new TMyThread(true,Edit1); //創(chuàng)建線程對(duì)象實(shí)例
thread2=new TMyThread(true,Edit2);
}
//---------------------------------------------------------------------------//Unit2.h //線程類頭文件//---------------------------------------------------------------------------
#ifndef Unit2H
#define Unit2H
//---------------------------------------------------------------------------
#include <Classes.hpp>
//---------------------------------------------------------------------------
class TMyThread : public TThread
{
private:
TEdit *edResult; //自定義局部變量
String strResult;
protected:
void __fastcall Execute();
void __fastcall ShowResult(); //自定義函數(shù)
public:
__fastcall TMyThread(bool CreateSuspended,TEdit *AEdit); //注意:修改了默認(rèn)參數(shù)
};
//---------------------------------------------------------------------------
#endif//Unit2.cpp //線程類實(shí)現(xiàn)文件//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop#include "Unit2.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------
__fastcall TMyThread::TMyThread(bool CreateSuspended,TEdit *AEdit)
: TThread(CreateSuspended)
{
edResult=AEdit;
}
//---------------------------------------------------------------------------
void __fastcall TMyThread::Execute()
{
for(int i=0;i<200;i++)
{
strResult=IntToStr(i);
Synchronize(ShowResult); //管理線程同步,保證安全性
Sleep(100);
}
}
//---------------------------------------------------------------------------
void __fastcall TMyThread::ShowResult()
{
edResult->Text=strResult;
}
//---------------------------------------------------------------------------
然后我們F9運(yùn)行程序就可以查看效果了。二、編寫(xiě)線程函數(shù)Execute()函數(shù)就是線程函數(shù),它包含了程序中所有需要并行執(zhí)行的代碼。除了共享相同的進(jìn)程空間外,可以認(rèn)為線程就是通過(guò)應(yīng)用程序啟動(dòng)的程序。但在 編寫(xiě)線程函數(shù)的時(shí)候需要注意與單獨(dú)程序的不同之處。因?yàn)榫€程與其他線程共享內(nèi)存空間,所以必須確認(rèn)沒(méi)有覆蓋應(yīng)用程序中其它線程的內(nèi)存地址。而另一方面,可 以使用共享內(nèi)存在線程之間進(jìn)行通信。在線程函數(shù)內(nèi)部,我們可以使用任意的全局變量,但有些變量我們并不希望同一線程類的其他實(shí)例共享它,就可以聲明一個(gè)線程(thread-local)變 量。通過(guò)將__thread修飾語(yǔ)加入變量聲明就可以聲明一個(gè)線程變量。例如 int __thread x; 聲明一個(gè)整型變量。__thread修飾語(yǔ)只可用于全局(文件范圍)或靜態(tài)變量。指針和函數(shù)變量不能作為線程變量。使用“在寫(xiě)入時(shí)復(fù)制”語(yǔ)法的類,如AnsiStrings也不能作為線程變量。需要在運(yùn)行時(shí)進(jìn)行初始化或析構(gòu)的類型也不能被聲明為_(kāi)_thread類型。當(dāng)程序中調(diào)用Resume()函數(shù)時(shí),線程啟動(dòng)并繼續(xù)執(zhí)行直到Execute()結(jié)束。這就是線程執(zhí)行特定任務(wù),并在其完成時(shí)終止的模式。然而,有時(shí)應(yīng)用 程序需在一些外部條件滿足時(shí)終止線程。通過(guò)檢查T(mén)erminated屬性可允許其它線程通知本線程終止。當(dāng)其它線程試圖終止本線程時(shí),它調(diào)用 Terminate()函數(shù)。Terminate()函數(shù)將本線程的Terminated屬性設(shè)置為true。Execute()函數(shù)通過(guò)檢查和響應(yīng) Terminated屬性來(lái)實(shí)現(xiàn)Terminate()函數(shù)。下面的實(shí)例演示了這種做法:
void __fastcall TMyThread::Execute()
{
while( !Terminated )
{
}
}在線程函數(shù)終止時(shí)我們可能需要做一些清理工作。由于在線程終止前,OnTerminate事件會(huì)發(fā)生,所以我們可以將清理代碼放在OnTerminate 事件處理程序中,這樣可確保不管Execute()函數(shù)如何執(zhí)行,清理代碼總是可以被執(zhí)行。要注意OnTerminate事件處理程序不作為線程的一部分 運(yùn)行,而是在應(yīng)用程序的主線程中執(zhí)行的。這意味著:
(1)在OnTerminate事件處理程序中不能使用任何線程局部變量。
(2)在OnTerminate事件處理程序中可安全地訪問(wèn)任何組件及VCL對(duì)象而不會(huì)和其他線程發(fā)生沖突。三、協(xié)調(diào)線程在編寫(xiě)線程執(zhí)行時(shí)運(yùn)行的代碼時(shí),必須考慮到可能同步執(zhí)行的其他線程行為。主要有兩種情況:一個(gè)是避免兩個(gè)線程試圖同時(shí)使用某一個(gè)全局對(duì)象或變量;另一個(gè)是某線程中的一些代碼可能會(huì)依賴其他線程所執(zhí)行任務(wù)的結(jié)果。1,避免同時(shí)訪問(wèn)
為避免在訪問(wèn)全局對(duì)象或變量時(shí)與其他線程發(fā)生沖突,可能需要暫停其他線程的執(zhí)行,直到該線程代碼完成操作。這里需要注意,不要暫停其他不需停止的線程執(zhí)行,這樣會(huì)使效率嚴(yán)重降低,也無(wú)法獲得使用多線程的優(yōu)點(diǎn)。<1>,鎖定對(duì)象
一些對(duì)象內(nèi)置了鎖定功能,以防止其他線程使用該對(duì)象的實(shí)例。例如,畫(huà)布對(duì)象(TCanvas及其派生類)有一種Lock()函數(shù)可防止其他線程訪問(wèn)畫(huà)布,直到調(diào)用Unlock()函數(shù)。
VCL還包含一種線程安全的列表對(duì)象TThreadList。調(diào)用TThreadList::LockList()返回列表對(duì)象,同時(shí)組織其他線程使用列 表直到調(diào)用UnlockList()函數(shù)。調(diào)用TCanvas::Lock()函數(shù)或TThreadList::LockList()函數(shù)時(shí)可以安全地嵌 套。鎖定直到最后一個(gè)鎖定調(diào)用匹配到同一線程中相應(yīng)的解鎖調(diào)用時(shí)才會(huì)被釋放。
顯然這種方法只對(duì)部分類有效。<2>,使用重要區(qū)段
若對(duì)象沒(méi)有提供內(nèi)置的鎖定功能,可使用重要區(qū)段。重要區(qū)段像門(mén)一樣,每次只允許一個(gè)線程進(jìn)入。要使用它,需創(chuàng)建TCriticalSection的全局實(shí) 例。TCriticalSection有兩個(gè)函數(shù):Acquire()(阻止其他線程執(zhí)行該區(qū)段)以Release()(取消對(duì)其他線程的阻止)。
每個(gè)重要區(qū)段都與需要保護(hù)的全局內(nèi)存關(guān)聯(lián)。每個(gè)要訪問(wèn)這個(gè)全局內(nèi)存的線程首先要調(diào)用Acquire()函數(shù)以確保其他線程不能訪問(wèn)它。當(dāng)線程結(jié)束時(shí),要調(diào)用Release()函數(shù)以便其他線程能繼續(xù)訪問(wèn)。
例如,應(yīng)用程序有一個(gè)全局重要區(qū)段變量pLockXY,可阻止訪問(wèn)全局變量X和Y。任何使用X或Y的線程必須調(diào)用重要區(qū)段,如下所示:
pLockXY->Acquire();
try{
Y=sin(X);
}
__finally
{
pLockXY->Release();
}<3>,使用多重讀、獨(dú)占寫(xiě)的同步器
當(dāng)使用重要區(qū)段來(lái)保護(hù)全局內(nèi)存時(shí),每次只有一個(gè)線程可以使用該內(nèi)容。這種保護(hù)可能超出了需要,特別是當(dāng)有一個(gè)經(jīng)常讀但很少寫(xiě)的對(duì)象或變量時(shí)更是如此。多個(gè) 線程同時(shí)讀相同內(nèi)存但沒(méi)有線程寫(xiě)內(nèi)存是沒(méi)有危險(xiǎn)的。當(dāng)有一些經(jīng)常被讀但很少有線程向其寫(xiě)入的全局內(nèi)存時(shí),可使用 TMultiReadExclusiveWriteSynchronizer對(duì)象保護(hù)它。這個(gè)對(duì)象與重要區(qū)段一樣,但它允許多個(gè)線程同時(shí)讀,只要沒(méi)有線程 寫(xiě)即可。線程必須有獨(dú)占訪問(wèn)權(quán)才能寫(xiě)使用TMultiReadExclusiveWriteSynchronizer保護(hù)的內(nèi)存。
要使用“多重讀、獨(dú)占寫(xiě)”的同步器,需創(chuàng)建TMultiReadExclusiveWriteSynchronizer的一個(gè)全局實(shí)例,它與要保護(hù)的全局 內(nèi)存關(guān)聯(lián)。每個(gè)需要讀內(nèi)存的線程首先要調(diào)用BeginRead()函數(shù)。它確保當(dāng)前無(wú)其它線程寫(xiě)內(nèi)存。線程完成讀操作后調(diào)用EndRead()函數(shù)。任何 線程要寫(xiě)內(nèi)存的時(shí)候必須先調(diào)用BeginWrite()函數(shù),結(jié)束后調(diào)用EndWrite()函數(shù)。<4>,共享內(nèi)存的其他技術(shù)
當(dāng)使用VCL對(duì)象時(shí),使用主VCL線程來(lái)執(zhí)行代碼,可確保對(duì)象不會(huì)間接地訪問(wèn)同時(shí)被其他線程中的VCL對(duì)象使用的內(nèi)存。若全局變量不需要被多個(gè)線程共享,可使用線程變量來(lái)代替它。線程可以不需要等待或暫停其他線程。2,等待其他線程
若線程必須等待另一線程完成某項(xiàng)任務(wù),可讓線程臨時(shí)中斷執(zhí)行。然后要么等待另一線程完全執(zhí)行結(jié)束,要么等到另一線程通知完成了該項(xiàng)任務(wù)。<1>,等待線程執(zhí)行結(jié)束
要等待另一線程執(zhí)行結(jié)束,使用它的WaitFor()函數(shù)。WaitFor()函數(shù)直到那個(gè)線程終止才返回,終止的方式要么完成了Execute()函數(shù),要么由于一個(gè)異常。例如,下面的代碼在訪問(wèn)列表中的對(duì)象前等待,直到另一線程填滿該列表。
void __fastcall TVisitList::Execute()
{
int fileRes;
TFillThread *fl=new TFillThread(false);
fillRes=f1->WaitFor();
//以下進(jìn)行后續(xù)處理
}
上例中,列表對(duì)象只在WaitFor()函數(shù)指出該列表被填滿時(shí)才能被訪問(wèn)。返回值由被等待線程的Execute()函數(shù)指定。然而,因?yàn)檎{(diào)用 WaitFor()函數(shù)的線程需要直到另一線程的執(zhí)行結(jié)果,無(wú)法以代碼調(diào)用Execute()函數(shù),Execute()函數(shù)也無(wú)法返回任何值。所以 TFillThread線程的Execute()函數(shù)應(yīng)該設(shè)置ReturnValue屬性。ReturnValue通過(guò)被其他線程調(diào)用的 WaitFor()函數(shù)返回。返回值是一個(gè)證書(shū),由應(yīng)用程序確定其含意。<2>,等待任務(wù)完成
有時(shí),只需等待線程完成一些操作而不是執(zhí)行結(jié)束。為此,可使用一個(gè)事件對(duì)象。事件對(duì)象(TEvent)應(yīng)具有全局范圍以便他們能夠?yàn)樗芯€程可見(jiàn)。當(dāng)一個(gè) 線程完成一個(gè)被其他線程依賴的操作時(shí),調(diào)用TEvent::SetEvent()函數(shù)。它發(fā)出一個(gè)信號(hào),以便任何其他線程可檢查并得知操作完成。要關(guān)掉信 號(hào)則使用ResetEvent()函數(shù)。四、調(diào)試多線程程序當(dāng)調(diào)試多線程應(yīng)用程序時(shí),試圖跟蹤所有并行線程的狀態(tài),或在斷點(diǎn)停止時(shí)判斷是哪一個(gè)線程的執(zhí)行往往會(huì)使人感到迷惑。可使用Thread Status框來(lái)幫助跟蹤并控制應(yīng)用程序中所有的線程。開(kāi)啟Thread Status框的方法是:View|Debug Windows|Threads。當(dāng)一個(gè)調(diào)試事件(斷點(diǎn)、異常、暫停)發(fā)生時(shí),線程狀態(tài)指示各個(gè)線程的狀態(tài)。右擊可定位相應(yīng)的源代碼位置或?qū)⑵渌€程設(shè)置為當(dāng)前線程等。最后要提醒的是,不要使用無(wú)意義的多線程。如果一段程序完全是串行的,每一步的操作都需要上一步的結(jié)果,那么在這里采用多線程技術(shù)就是毫無(wú)意義的。[喝小酒的網(wǎng)摘]http://blog.hehehehehe.cn/a/8498.htm
?
轉(zhuǎn)載于:https://www.cnblogs.com/mypsq/p/5121138.html
總結(jié)
以上是生活随笔為你收集整理的BCB 多线程的同步与协调的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: UITextView,UITextFie
- 下一篇: BZOJ-2756 奇怪的游戏