日本好好热aⅴ|国产99视频精品免费观看|日本成人aV在线|久热香蕉国产在线

  • <cite id="ikgdy"><table id="ikgdy"></table></cite>
    1. 西西軟件下載最安全的下載網(wǎng)站、值得信賴的軟件下載站!

      首頁編程開發(fā)Delphi → 多線程的基本概念和Delphi線程對象Tthread介紹

      多線程的基本概念和Delphi線程對象Tthread介紹

      相關(guān)軟件相關(guān)文章發(fā)表評論 來源:西西整理時間:2013/3/4 9:32:27字體大。A-A+

      作者:西西點(diǎn)擊:0次評論:0次標(biāo)簽: 多線程

      • 類型:服務(wù)器區(qū)大。21KB語言:中文 評分:6.6
      • 標(biāo)簽:
      立即下載

      WIN 98/NT/2000/XP是個多任務(wù)操作系統(tǒng),也就是:一個進(jìn)程可以劃分為多個線程,每個線程輪流占用CPU運(yùn)行時間和資源,或者說,把CPU 時間劃成片,每個片分給不同的線程,這樣,每個線程輪流的“掛起”和“喚醒”,由于時間片很小,給人的感覺是同時運(yùn)行的。

      多線程帶來如下好處:
      1)避免瓶頸;
      2)并行操作;
      3)提高效率;

      多線程的兩個概念:
      1) 進(jìn)程:也稱任務(wù),程序載入內(nèi)存,并分配資源,稱為“一個進(jìn)程”。
      注意:進(jìn)程本身并不一定要正在執(zhí)行。進(jìn)程由以下幾部分組成:
      a>一個私有的地址空間,它是進(jìn)程可以使用的一組虛擬內(nèi)存地址空間;
      b>程序的相關(guān)代碼、數(shù)據(jù)源;
      c>系統(tǒng)資源,比如操作系統(tǒng)同步對象等;
      d>至少包含一個線程(主線程);

      2) 線程:是進(jìn)程的執(zhí)行單位(線程本身并不包括程序代碼,真正擁有代碼的是進(jìn)程),是操作系統(tǒng)分配CPU時間的基本實(shí)體,每個進(jìn)程至少包括一個線程,稱為主線程。一個進(jìn)程如果有多個線程,就可以共享同一進(jìn)程的資源,并可以并發(fā)執(zhí)行。通俗點(diǎn)說就是進(jìn)程中一段并發(fā)運(yùn)行的代碼(一個函數(shù)或過程)。

      線程主要由如下兩部分組成:
      a>數(shù)據(jù)結(jié)構(gòu);
      b>CPU 寄存器和堆棧;

      線程函數(shù)運(yùn)行,啟動函數(shù)就返回了,主線程繼續(xù)向下執(zhí)行,而線程函數(shù)在一個獨(dú)立的線程中執(zhí)行,它要執(zhí)行多久,什么時候返回,主線程是不管也不知道的。

      一、Delphi線程對象--- Tthread

      雖然Windows提供了較多的多線程設(shè)計的API 函數(shù),但是直接使用API 函數(shù)極其不方便,而且使用不當(dāng)還容易出錯。為解決這個問題,Borland公司率先推出了一種Tthread 對象,來解決多線程設(shè)計上的困難,簡化了多線程問題的處理。

      一、Tthread對象的主要方法

      構(gòu)造線程:

      constructor Create(CreateSuspended:boolean)

      CreateSuspended=true構(gòu)造但不喚醒 ;false構(gòu)造的同時即喚醒 。

      掛起線程:
      suspend:(把線程掛起的次數(shù)加一)


      喚醒線程:
      resume :(注意:注意這個屬性是把線程掛起的次數(shù)減一,當(dāng)次數(shù)為0時,即喚醒。也就是說,線程掛起多少次,喚醒也需要多少次。同時掛起的時候?qū)⒈3志程的地址指針不變,所以線程掛起后再喚醒,將從掛起的地方開始運(yùn)行)

      析構(gòu)(清除線程所占用的內(nèi)存):
      destroy

      終止線程
      Terminate 

      使用這個類也很簡單,基本用法是:先從TThread派生一個自己的線程類(因?yàn)門Thread
      是一個抽象類,不能生成實(shí)例),然后是覆蓋(Override)抽象方法:Execute(這就是線程函數(shù),也就是在線程中執(zhí)行的代碼部分),如果需要用到可視VCL對象,還需要通過Synchronize過程進(jìn)行。

      線程的終止和退出:

      1)自動退出:

      一個線程從Execute()過程中退出,即意味著線程的終止,此時將調(diào)用Windows的ExitThread()函數(shù)來清除線程所占用的堆棧。

      如果線程對象的 FreeOnTerminate屬性設(shè)為True,則線程對象將自動刪除,并釋放線程所占用的資源。
      這是消除線程對象最簡單的辦法。

      2)受控退出:

      利用線程對象的Terminate屬性,可以由進(jìn)程或者由其他線程控制線程的退出。只需要簡單的調(diào)用該線程的Terminate方法,并設(shè)置線程對象的Terminate屬性為True。
        一般來說,在線程中,應(yīng)該不斷監(jiān)視Terminate的值,一旦發(fā)現(xiàn)為True,則退出,一般來說,例如在Execute()過程中可以這樣寫:

      While not Terminate do 
      begin 
      ........ 
      end;

      3)退出的API函數(shù):

      關(guān)于線程退出的API函數(shù)聲明如下:

      Function TerminateThread(hThread:Thandle;dwExitCode:DWORD);

      不過,這個函數(shù)會使代碼立刻終止,而不管程序中有沒有

      try....finally

      機(jī)制,可能會導(dǎo)致錯誤,不到萬不得已,最好不要使用。

      4)利用掛起線程的方法(suspend)

      利用掛起線程的suspend方法,后面跟個Free,也可以釋放線程,
      例如:

      thread1.suspend; //掛起

      thread2.free; //釋放

      二、多線程的同步機(jī)制

      同步機(jī)制,研究多線程的同步機(jī)制的必要性在于,多線程同步工作時,如果同時調(diào)用相同的資源,就可能會出現(xiàn)問題,如對全局變量、數(shù)據(jù)庫操作發(fā)生沖突,甚至產(chǎn)生死鎖和競爭問題。

      舉個發(fā)生沖突的實(shí)例看一下:

      一般來說,對內(nèi)存數(shù)據(jù)加一的操作分解以后有三個步驟:
      1、從內(nèi)存中讀出數(shù)據(jù)
      2、數(shù)據(jù)加一
      3、存入內(nèi)存
      現(xiàn)在假設(shè)在一個兩個線程的應(yīng)用中用Inc進(jìn)行加一操作可能出現(xiàn)的一種情況:
      1、線程A從內(nèi)存中讀出數(shù)據(jù)(假設(shè)為3)
      2、線程B從內(nèi)存中讀出數(shù)據(jù)(也是3)
      3、線程A對數(shù)據(jù)加一(現(xiàn)在是4)
      4、線程B對數(shù)據(jù)加一(現(xiàn)在也是4)
      5、線程A將數(shù)據(jù)存入內(nèi)存(現(xiàn)在內(nèi)存中的數(shù)據(jù)是4)
      6、線程B也將數(shù)據(jù)存入內(nèi)存(現(xiàn)在內(nèi)存中的數(shù)據(jù)還是4,但兩個線程都對它加了一,應(yīng)該是5才對,所以這里出現(xiàn)了錯誤的結(jié)果)

      1.臨界區(qū)(Critical Sections)

      臨界區(qū)(CriticalSection)是一項共享數(shù)據(jù)訪問保護(hù)的技術(shù)。對它只有兩個操作:Enter和Leave,這兩個操作也是原子操作。

      它的保護(hù)原理是這樣的:當(dāng)一個線程A調(diào)用某一個Enter后,開始訪問某個數(shù)據(jù)D,如果此時另一個線程B也要訪問數(shù)據(jù)D,則它會在調(diào)用這個Enter時,發(fā)現(xiàn)已經(jīng)有線程進(jìn)入臨界區(qū),然后線程B就會被掛起,等待線程A調(diào)用Leave。當(dāng)線程A完成操作,調(diào)用Leave離開后,線程B就會被喚醒,并設(shè)置臨界區(qū)標(biāo)志,開始操作數(shù)據(jù),這樣就防止了訪問沖突

      var
      CS:TRTLCriticalSection;//被聲明在程序最上方,作為線程都可以使用的全局變量。
      initializeCriticalSection(cs); //初始化

      Procedure InterlockedIncrement( var aValue : Integer );
      Begin
        
          EnterCriticalSection(CS);//獨(dú)占
          Inc( aValue );
          LeaveCriticalSection(CS); //解除獨(dú)占
      End;

      現(xiàn)在再來看前面那個例子:
      1. 線程A進(jìn)入臨界區(qū)(假設(shè)數(shù)據(jù)為3)
      2. 線程B進(jìn)入臨界區(qū),因?yàn)锳已經(jīng)在臨界區(qū)中,所以B被掛起
      3. 線程A對數(shù)據(jù)加一(現(xiàn)在是4)
      4. 線程A離開臨界區(qū),喚醒線程B(現(xiàn)在內(nèi)存中的數(shù)據(jù)是4)
      5. 線程B被喚醒,對數(shù)據(jù)加一(現(xiàn)在就是5了)
      6. 線程B離開臨界區(qū),現(xiàn)在的數(shù)據(jù)就是正確的了。
      臨界區(qū)就是這樣保護(hù)共享數(shù)據(jù)的訪問

      請注意,臨界區(qū)只能在一個進(jìn)程內(nèi)使用,可以在多處設(shè)置調(diào)用enter。

      不要長時間鎖住一份資源,如果你一直讓資源被鎖定,你就會阻止其它線程的執(zhí)行,并把整個程序帶到一個完全停止的狀態(tài),所以千萬不要在一個ciritical section中調(diào)用sleep()或任何Wait…()函數(shù)。

      ciritical section的一個缺點(diǎn)是,它不是核心對象,如果進(jìn)入ciritical section的那個線程結(jié)束了或者當(dāng)?shù)袅,而沒有調(diào)用LeaveCriticalSection的話,系統(tǒng)沒有辦法將該ciritical section清除,如果你需要這樣的機(jī)能,你應(yīng)該使用mutex。

      VOID InitializeCriticalSection(

        LPCRITICAL_SECTION lpCriticalSection//一個指針,指向欲被初始化的CRITICAL_SECTION變量

        );

      函數(shù)功能:初始化一個臨界對象,當(dāng)你用畢臨界對象時,必須調(diào)用DeleteCriticalSection()清除它。

      VOID DeleteCriticalSection (
      LPCRITICAL_SECTION lpCriticalSection// 臨界對象指針  
      );

      函數(shù)功能:申請刪除臨界對象

      VOID EnterCriticalSection(

        LPCRITICAL_SECTION lpCriticalSection//臨界對象指針

        );

      函數(shù)功能:申請進(jìn)入臨界對象

      VOID LeaveCriticalSection(

        LPCRITICAL_SECTION lpCriticalSection//臨界對象指針

        );

      函數(shù)功能

      申請進(jìn)入臨界對象

      2.互斥器(Mutexes)

      一個時間內(nèi)只能夠有一個線程擁有mutex,就好像同一個時間只能夠有一個線程進(jìn)入同一臨界區(qū)。

      Mutex和critical section還是有差別的:

      1.鎖住一個未被使用的Mutexes,比鎖住一個未被使用的critical section,需要花費(fèi)幾乎100倍的時間

      2. Mutexes可以跨進(jìn)程使用,critical section則只能夠在同一個進(jìn)程中使用

      3.等待一個Mutexes時,你可以指定結(jié)束等待的時間長度,但對于critical section則不行。

      兩種對象的相關(guān)函數(shù)比較:

      CRITICAL_SECTIONMutex核心對象
      InitializeCriticalSection()CreateMutex()
       OpenMutex()
      EnterCriticalSection()WaitForSingleObject()
       WaitForMultipleObject()
       MsgWaitForMutipleObjects()
      LeaveCriticalSection()ReleaseMutex()
      DeleteCriticalSection()CloseHandle()

      Mutex的使用機(jī)制:

      1.      有一個mutex,此時沒有任何線程擁有它,此時它處于非激發(fā)狀態(tài)。

      2.      某個線程調(diào)用WaitforSingleObject()或任何其它的wait…函數(shù),并指定該mutex的handle為參數(shù)

      3.      win32于是將該mutex的擁有權(quán)給予這個線程,然后將此mutex設(shè)為激發(fā)狀態(tài),于是wait..函數(shù)返回

      4.      wait..函數(shù)返回后,win32立刻又將mutex設(shè)為非激發(fā)狀態(tài),是任何處于等待狀態(tài)下的其它線程沒有辦法獲得其擁有權(quán)

      5.      獲得該mutex的線程調(diào)用Release,將mutex釋放掉。于是循環(huán)到第一步。

      如果線程擁有一個mutex,而在結(jié)束前沒有調(diào)用releaseMutex,mutex不會被摧毀,該mutex會被win32視為“未被擁有”以及“未被激發(fā)”,下一個等待中的線程會被以WAIT_ABANDONED_0通知。如果是WaitForMultipleObjects()等待辭mutex,函數(shù)返回值介于WAIT_ABANDONED_0和WAIT_ABANDONED_0+n之間,n是指handle數(shù)組的元素個數(shù)。

      HANDLE CreateMutex(
       LPSECURITY_ATTRIBUTES lpMutexAttributes,
       BOOL bInitialOwner,
       LPCTSTR lpName
      );

      參數(shù)  

      lpMutexAttributes:安全屬性。Null表示使用默認(rèn)的屬性。

      bInitialOwner:如果你希望調(diào)用這個函數(shù)的線程擁有mutex,就將此值設(shè)為true

      lpName:互斥對象的名稱

      返回值

       如果成功,則返回一個handle,否則返回null。

      函數(shù)說明:

      如果指定的mutex名稱已經(jīng)存在,win32會給你一個mutex handle,而不會為你產(chǎn)生一個新的mutex。調(diào)用GetLastError會傳回ERROR_ALREADY_EXISTS。當(dāng)你不需要mutex時,你可以調(diào)用closehandle()將它關(guān)閉。

      BOOL ReleaseMutex(

      HANDLE hMutex //欲釋放mutex的handle

      );

      返回值

       如果成功,則返回true,否則返回false。

      3.信號量(Semaphores)

      Mutex是semaphore的一種退化,如果你產(chǎn)生一個semaphore并令最大值為1,那它就是個mutex。因此,mutex又常被稱為binary semaphore。在許多系統(tǒng)中,semaphore常被使用,因?yàn)閙utex可能并不存在,在win32中semaphore被使用的情況就少得多,因?yàn)閙utex存在的原因。

      一旦semaphore的現(xiàn)值降到0,就表示資源已經(jīng)耗盡。此時任何線程如果調(diào)用Wait…函數(shù),必然要等待,直到某個鎖定被解除。

      HANDLE CreateSemaphore(

      LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,

       LONG lInitialCount,

      LONG lMaximumCount,

       LPCTSTR lpName  

      )

      參數(shù):

      lpSemaphoreAttributes:安全屬性,null表示使用默認(rèn)屬性。

      lInitialCount:初始值,必須>=0,并且<= lMaximumCount

      lMaximumCount:最大值,也就是在同一時間內(nèi)能夠鎖住semaphore的線程數(shù)

      lpName:名稱,這個值也可以是null。

      返回值:

       如果成功就返回一個handle,否則返回null。如果semaphore名稱已經(jīng)存在,函數(shù)還是會成功,GetLastError會返回ERROR_ALREADY_EXISTS

      函數(shù)說明:

      產(chǎn)生一個semaphore。

      BOOL ReleaseSemaphore(

      HANDLE hSemaphore,

      LONG lReleaseCount,

      LPLONG lpPreviousCount

      );

      參數(shù):

      hSemaphore:semaphore的句柄。

      lReleaseCount:semaphore現(xiàn)值的增量,通常是1,該值不可以是負(fù)值或者0

      lpPreviousCount:返回semaphore增加前的現(xiàn)值

      返回值:

       如果成功就返回true,否則返回false。

      函數(shù)說明:

      三、事件(Events)

      事件(Event)是一種核心對象,它的唯一目的就是成為激發(fā)狀態(tài)或未激發(fā)狀態(tài)。這兩種狀態(tài)完全在你的掌握之下,不會因?yàn)閃ait…函數(shù)的調(diào)用而變化。

      HANDLE     CreateEvent(   
            LPSECURITY_ATTRIBUTES       lpEventAttributes,        
            BOOL       bManualReset,                                                  
            BOOL       bInitialState,   

            LPCTSTR       lpName      

            );       

      參數(shù):

           lpEventAttributes:安全屬性,null表示使用默認(rèn)屬性。

           bManualReset :

      此值為false,表示event變成激發(fā)狀態(tài)后,自動重置為非激發(fā)狀態(tài);

      此值為true,表示不會自動重置,必須靠程序(調(diào)用ResetEvent)操作才能將激發(fā)狀態(tài)的event重置為非激發(fā)狀態(tài)。

        bInitialState :初始狀態(tài),true一開始處于激發(fā)狀態(tài),false一開始處于非激發(fā)狀態(tài)

         lpName :Event對象名   

      返回值:

      如果成功就返回一個handle,否則返回null。如果event名稱已經(jīng)存在,函數(shù)還是會成功,GetLastError會返回ERROR_ALREADY_EXISTS

      BOOL SetEvent(HANDLE hEvent);

      //把event對象設(shè)為激發(fā)狀態(tài)

      BOOL ResetEvent(HANDLE hEvent);

      //把event對象設(shè)為非激發(fā)狀態(tài)

      BOOL PulseEvent(HANDLE hEvent );

      //如果event的bManualReset 為true:把event對象設(shè)為激發(fā)狀態(tài),喚醒所有等待中的線程,然后event恢復(fù)為非激發(fā)狀態(tài)

      //如果event的bManualReset 為false:把event對象設(shè)為激發(fā)狀態(tài),喚醒一個等待中的線程,然后event恢復(fù)為非激發(fā)狀態(tài)

      5.使用Synchronize方法

      這個方法用于訪問VCL主線程所管理的資源,其方法的應(yīng)用是:
      第一步:把訪問主窗口(或主窗口控件資源)的代碼放到線程的一個方法中;
      第二步:是在線程對象的Execute方法中,通過Synchronize方法使用該方法。
      實(shí)例:
      procedure Theater.Execute; 
      begin 
      Synchronize(update); 
      end; 

      procedure Theater.update; 
      begin 
      ......... 
      end; 

      這里通過 Synchronize使線程方法update同步。

      6、使用VCL類的Look方法

      在Delphi的IDE提供的構(gòu)件中,有一些對象內(nèi)部提供了線程的同步機(jī)制,工作線程可以直接使用這些控件,比如:Tfont,Tpen,TBitmap,TMetafile,Ticon等。另外,一個很重要的控件對象叫TCanvas,提供了一個Lock方法用于線程的同步,當(dāng)一個線程使用此控件對象的時候,首先調(diào)用這個對象的Lock方法,然后對這個控件進(jìn)行操作,完畢后再調(diào)用Unlock方法,釋放對控間的控制權(quán)。
      例如:
      CanversObject.look; 
      try 
      畫圖
      finally 
      CanversObject.unlock; 
      end; 
      {使用這個保護(hù)機(jī)制,保證不論有沒有異常,unlock都會被執(zhí)行否則很可能會發(fā)生死鎖。在多線程設(shè)計的時候,應(yīng)該很注意發(fā)生死鎖的問題}  

      四、線程的優(yōu)先級:

      在多線程的情況下,一般要根據(jù)線程執(zhí)行任務(wù)的重要性,給線程適當(dāng)?shù)膬?yōu)先級,一般如果量的線程同時申請CPU時間,優(yōu)先級高的線程優(yōu)先。

      優(yōu)先權(quán)類別(Priority Class)

      Win32提供四種優(yōu)先權(quán)類別,每一個類別對應(yīng)一個基本的優(yōu)先權(quán)層次。

      表格5-1優(yōu)先權(quán)類別

      優(yōu)先權(quán)類別基礎(chǔ)優(yōu)先權(quán)值
      HIGH_PRIORITY_CLASS13
      IDLE_PRIORITY_CLASS4
      NORMAL_PRIORITY_CLASS7or8
      REALTIME_PRIORITY_CLASS24

      大部分程序使用NORMAL_PRIORITY_CLASS。優(yōu)先權(quán)類別是進(jìn)程的屬性之一,利用SetPriorityClass和GetPriorityClass函數(shù)可以調(diào)整和獲取該值。

      優(yōu)先權(quán)層次(priority Level)

      線程的優(yōu)先權(quán)層次使你能夠調(diào)整同一個進(jìn)程內(nèi)的各線程的相對重要性。一共七種優(yōu)先權(quán)層次:

      表格5-2

      優(yōu)先權(quán)層次調(diào)整值
      THREAD_PRIORITY_LOWEST-2
      THREAD_PRIORITY_BELOW_NORMAL-1
      THREAD_PRIORITY_NORMAL0
      THREAD_PRIORITY_ABOVE_NORMAL+1
      THREAD_PEIOEITY_HIGHEST+2
      THREAD_PRIORITY_IDLE1
      THREAD_PRIORITY_TIME_CRITICAL15

      利用SetThreadPriority和GetThreadPriority函數(shù)可以調(diào)整和獲取該值

      在Windows下,給線程的優(yōu)先級分為30級,而Delphi中Tthread對象相對簡單的把優(yōu)先級分為七級。也就是在Tthread中聲明了一個枚舉類型TTthreadPriority:

      type 
      TTthreadPriority(tpidle,tpLowest,tpLower,tpNormal, 
      tpHight,tpHighest,tpTimecrital) 

      分別對應(yīng)的是最低(系統(tǒng)空閑時有效,-15),較低(-2),低(-1),正常(普通0),高(1),較高(2),最高(15)。
      設(shè)置優(yōu)先級可使用thread對象的priority屬性:
      threadObject.priority:=Tthreadpriority(級別);


      BOOL SetThreadPriority(

      HANDLE hThread,  //欲調(diào)整優(yōu)先權(quán)的那個線程的句柄

      int nPriority         //表格5-2所顯示的值

      );

      Int GetThreadPriority(

         HANDLE hThread   //線程的句柄

          );

      返回值是線程的優(yōu)先級。

        相關(guān)評論

        閱讀本文后您有什么感想? 已有人給出評價!

        • 8 喜歡喜歡
        • 3 頂
        • 1 難過難過
        • 5 囧
        • 3 圍觀圍觀
        • 2 無聊無聊

        熱門評論

        最新評論

        發(fā)表評論 查看所有評論(0)

        昵稱:
        表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
        字?jǐn)?shù): 0/500 (您的評論需要經(jīng)過審核才能顯示)
        推薦文章

        沒有數(shù)據(jù)