談笑有鴻儒,往來無白丁

          在恰當的時間、地點以恰當的方式表達給恰當的人...  閱讀的時候請注意分類,佛曰我日里面是談笑文章,其他是各個分類的文章,積極的熱情投入到寫博的隊伍中來,支持blogjava做大做強!向dudu站長致敬>> > 我的微博敬請收聽
          ?????? 多核時代的到來,對于我們程序員來說要盡快的,盡可能多的使用多線程編程只有這樣,作的程序才會有高效率,這個思想一定要宣傳啊,不然多核時代了,還寫單線程的程序就太不夠檔次了。

          delphi中多線程同步的一些方法

          當有多個線程的時候,經常需要去同步這些線程以訪問同一個數據或資源。例如,假設有一個程序,其中一個線程用于把文件讀到內存,而另一個線程用于統計文件中的字符數。當然,在把整個文件調入內存之前,統計它的計數是沒有意義的。但是,由于每個操作都有自己的線程,操作系統會把兩個線程當作是互不相干的任務分別執行,這樣就可能在沒有把整個文件裝入內存時統計字數。為解決此問題,你必須使兩個線程同步工作。
          存在一些線程同步地址的問題,Win32提供了許多線程同步的方式。在本節你將看到使用臨界區、 互斥、信號量和事件來解決線程同步的問題。

          1. 臨界區
          臨界區是一種最直接的線程同步方式。所謂臨界區,就是一次只能由一個線程來執行的一段代碼。如果把初始化數組的代碼放在臨界區內,另一個線程在第一個線程處理完之前是不會被執行的。
          在使用臨界區之前,必須使用InitializeCriticalSection()過程來初始化它。
          其聲明如下:
          ????procedure InitializeCriticalSection(var
          lpCriticalSection參數是一個TRTLCriticalSection類型的記錄,并且是變參。至于TRTLCriticalSection 是如何定義的,這并不重要,因為很少需要查看這個記錄中的具體內容。只需要在lpCriticalSection中傳遞未初始化的記錄,InitializeCriticalSection()過程就會填充這個記錄。
          注意Microsoft故意隱瞞了TRTLCriticalSection的細節。因為,其內容在不同的硬件平臺上是不同的。在基于Intel的平臺上,TRTLCriticalSection包含一個計數器、一個指示當前線程句柄的域和一個系統事件的句柄。在Alpha平臺上,計數器被替換為一種Alpha-CPU 數據結構,稱為spinlock。在記錄被填充后,我們就可以開始創建臨界區了。這時我們需要用EnterCriticalSection()和LeaveCriticalSection()來封裝代碼塊。這兩個過程的聲明如下:
          ???
          procedure EnterCriticalSection(var lpCriticalSection:TRRLCriticalSection);stdcall;
          procedure LeaveCriticalSection(var
          正如你所想的,參數lpCriticalSection就是由InitializeCriticalSection()填充的記錄。
          當你不需要TRTLCriticalSection記錄時,應當調用DeleteCriticalSection()過程,下面是它的聲明:
          procedure DeleteCriticalSection(var


          2. 互斥
          互斥非常類似于臨界區,除了兩個關鍵的區別:首先,互斥可用于跨進程的線程同步。其次,互斥能被賦予一個字符串名字,并且通過引用此名字創建現有互斥對象的附加句柄。
          提示臨界區與事件對象(比如互斥對象)的最大的區別是在性能上。臨界區在沒有線程沖突時,要用1 0 ~ 1 5個時間片,而事件對象由于涉及到系統內核要用400~600個時間片。
          可以調用函數CreateMutex ( )來創建一個互斥量。下面是函數的聲明:
          function

          lpMutexAttributes參數為一個指向TSecurityAttributtes記錄的指針。此參數通常設為0,表示默認的安全屬性。bInitalOwner參數表示創建互斥對象的線程是否要成為此互斥對象的擁有者。當此參數為False時, 表示互斥對象沒有擁有者。
          lpName參數指定互斥對象的名稱。設為nil表示無命名,如果參數不是設為nil,函數會搜索是否有同名的互斥對象存在。如果有,函數就會返回同名互斥對象的句柄。否則,就新創建一個互斥對象并返回其句柄。
          當使用完互斥對象時,應當調用CloseHandle()來關閉它。

          在程序中使用WaitForSingleObject()來防止其他線程進入同步區域的代碼。此函數聲明如下:
          function


          這個函數可以使當前線程在dwMilliseconds指定的時間內睡眠,直到hHandle參數指定的對象進入發信號狀態為止。一個互斥對象不再被線程擁有時,它就進入發信號狀態。當一個進程要終止時,它就進入發信號狀態。dwMilliseconds參數可以設為0,這意味著只檢查hHandle參數指定的對象是否處于發信號狀態,而后立即返回。dwMilliseconds參數設為INFINITE,表示如果信號不出現將一直等下去。
          這個函數的返回值如下
          WaitFor SingleObject()函數使用的返回值
          返回值 含義
          WAIT_ABANDONED 指定的對象是互斥對象,并且擁有這個互斥對象的線程在沒有釋放此對象之前就已終止。此時就稱互斥對象被拋棄。這種情況下,這個互斥對象歸當前線程所有,并把它設為非發信號狀態
          WAIT_OBJECT_0 指定的對象處于發信號狀態
          WAIT_TIMEOUT等待的時間已過,對象仍然是非發信號狀態再次聲明,當一個互斥對象不再被一個線程所擁有,它就處于發信號狀態。此時首先調用WaitForSingleObject()函數的線程就成為該互斥對象的擁有者,此互斥對象設為不發信號狀態。當線程調用ReleaseMutex()函數并傳遞一個互斥對象的句柄作為參數時,這種擁有關系就被解除,互斥對象重新進入發信號狀態。
          注意除WaitForSingleObject()函數外,你還可以使用WaitForMultipleObject()和MsgWaitForMultipleObject()函數,它們可以等待幾個對象變為發信號狀態。這兩個函數的詳細情況請看Win32 API聯機文檔。

          3. 信號量
          另一種使線程同步的技術是使用信號量對象。它是在互斥的基礎上建立的,但信號量增加了資源計數的功能,預定數目的線程允許同時進入要同步的代碼??梢杂肅reateSemaphore()來創建一個信號量對象,其聲明如下:
          function

          和CreateMutex()函數一樣,CreateSemaphore()的第一個參數也是一個指向TSecurityAttribute s記錄的指針,此參數的缺省值可以設為nil。
          lInitialCount參數用來指定一個信號量的初始計數值,這個值必須在0和lMaximumCount之間。此參數大于0,就表示信號量處于發信號狀態。當調用WaitForSingleObject()函數(或其他函數)時,此計數值就減1。當調用ReleaseSemaphore()時,此計數值加1。
          參數lMaximumCount指定計數值的最大值。如果這個信號量代表某種資源,那么這個值代表可用資源總數。
          參數lpName用于給出信號量對象的名稱,它類似于CreateMutex()函數的lpName參數。

          ——————————————————————————————————————————

          ★★★關于線程同步:
          Synchronize()是在一個隱蔽的窗口里運行,如果在這里你的任務很繁忙,你的主窗口會阻塞掉;Synchronize()只是將該線程的代碼放到主線程中運行,并非線程同步。

          臨界區是一個進程里的所有線程同步的最好辦法,他不是系統級的,只是進程級的,也就是說他可能利用進程內的一些標志來保證該進程內的線程同步,據Richter說是一個記數循環;臨界區只能在同一進程內使用;臨界區只能無限期等待,不過2k增加了TryEnterCriticalSection函數實現0時間等待。

          互斥則是保證多進程間的線程同步,他是利用系統內核對象來保證同步的。由于系統內核對象可以是有名字的,因此多個進程間可以利用這個有名字的內核對象保證系統資源的線程安全性。互斥量是Win32 內核對象,由操作系統負責管理;互斥量可以使用WaitForSingleObject實現無限等待,0時間等待和任意時間等待。

          1. 臨界區
          臨界區是一種最直接的線程同步方式。所謂臨界區,就是一次只能由一個線程來執行的一段代碼。如果把初始化數組的代碼放在臨界區內,另一個線程在第一個線程處理完之前是不會被執行的。在使用臨界區之前,必須使用InitializeCriticalSection()過程來初始化它。
          在第一個線程調用了EnterCriticalSection()之后,所有別的線程就不能再進入代碼塊。下一個線程要等第一個線程調用LeaveCriticalSection()后才能被喚醒。

          2. 互斥
          互斥非常類似于臨界區,除了兩個關鍵的區別:首先,互斥可用于跨進程的線程同步。其次,互斥能被賦予一個字符串名字,并且通過引用此名字創建現有互斥對象的附加句柄。
          提示:臨界區與事件對象(比如互斥對象)的最大的區別是在性能上。臨界區在沒有線程沖突時,要用10 ~ 15個時間片,而事件對象由于涉及到系統內核要用400~600個時間片。
          當一個互斥對象不再被一個線程所擁有,它就處于發信號狀態。此時首先調用WaitForSingleObject()函數的線程就成為該互斥對象的擁有者,此互斥對象設為不發信號狀態。當線程調用ReleaseMutex()函數并傳遞一個互斥對象的句柄作為參數時,這種擁有關系就被解除,互斥對象重新進入發信號狀態。
          可以調用函數CreateMutex()來創建一個互斥量。當使用完互斥對象時,應當調用CloseHandle()來關閉它。

          3. 信號量
          另一種使線程同步的技術是使用信號量對象。它是在互斥的基礎上建立的,但信號量增加了資源計數的功能,預定數目的線程允許同時進入要同步的代碼??梢杂肅reateSemaphore()來創建一個信號量對象,
          因為只允許一個線程進入要同步的代碼,所以信號量的最大計數值(lMaximumCount)要設為1。ReleaseSemaphore()函數將使信號量對象的計數加1;
          記住,最后一定要調用CloseHandle()函數來釋放由CreateSemaphore()創建的信號量對象的句柄。

          ★★★WaitForSingleObject函數的返值:
          WAIT_ABANDONED指定的對象是互斥對象,并且擁有這個互斥對象的線程在沒有釋放此對象之前就已終止。此時就稱互斥對象被拋棄。這種情況下,這個互斥對象歸當前線程所有,并把它設為非發信號狀態;
          WAIT_OBJECT_0 指定的對象處于發信號狀態;
          WAIT_TIMEOUT等待的時間已過,對象仍然是非發信號狀態;

          ——————————————————————————————————————————————
          VCL支持三種技術來達到這個目的:
          (2) 使用critical區
          如果對象沒有提高內置的鎖定功能,需要使用critical區,Critical區在同一個時間只也許一個線程進入。為了使用Critical區,產生一個TCriticalSection全局的實例。TcriticalSection有兩個方法,Acquire(阻止其他線程執行該區域)和Release(取消阻止)

            每個Critical區是與你想要保護的全局內存相關聯。每個訪問全局內存的線程必須首先使用Acquire來保證沒有其他線程使用它。完成以后,線程調用Release方法,讓其他線程也可以通過調用Acquire來使用這塊全局內存。

            警告:Critical區只有在所有的線程都使用它來訪問全局內存,如果有線程直接調用內存,而不通過Acquire,會造成同時訪問的問題。例如:LockXY是一個全局的Critical區變量。任何一個訪問全局X, Y的變量的線程,在訪問前,都必須使用Acquire
          LockXY.Acquire; { lock out other threads }
          try
          Y := sin(X);
          finally
          LockXY.Release;
          end
          臨界區主要是為實現線程之間同步的,但是使用的時候注意,一定要在用此臨界對象同步的線程之外建立該對象(一般在主線程中建立臨界對象)。

          ————————————————————————————————————————————————
          線程同步使用臨界區,進程同步使用互斥對象。

          Delphi中封裝了臨界對象。對象名為TCriticalSection,使用的時候只要在主線程當中建立這個臨界對象(注意一定要在需要同步的線程之外建立這個對象)。具體同步的時候使用Lock和Unlock即可。
          而進程間同步建立互斥對象,則只需要建立一個互斥對象CreateMutex. 需要同步的時候只需要WaitForSingleObject(mutexhandle, INFINITE) unlock的時候只需要ReleaseMutex(mutexhandle);即可。

          有很多方法, 信號燈, 臨界區, 互斥對象,此外, windows下還可以用全局原子,共享內存等等. 在windows體系中, 讀寫一個8位整數時原子的, 你可以依靠這一點完成互斥的方法. 對于能夠產生全局名稱的方法能夠可以在進程間同步上(如互斥對象), 也可以用在線程間同步上;不能夠產生全局名稱的方法(如臨界區)只能用在線程間同步上.
          posted on 2006-12-11 09:26 壞男孩 閱讀(4813) 評論(0)  編輯  收藏 所屬分類: DELPHI
          主站蜘蛛池模板: 富川| 瓮安县| 博兴县| 离岛区| 宣化县| 固阳县| 佛坪县| 蒲城县| 扶风县| 拜城县| 秭归县| 建始县| 安乡县| 临颍县| 松江区| 白朗县| 宁津县| 浦东新区| 石柱| 濮阳县| 山丹县| 怀集县| 肥乡县| 福州市| 塘沽区| 巴中市| 阿勒泰市| 平顶山市| 若羌县| 江华| 平湖市| 灌阳县| 昌平区| 高淳县| 临安市| 铜鼓县| 吴江市| 利辛县| 丰镇市| 岚皋县| 黎平县|