WIN32下DELPHI中的多線程【同步1】(四)

線程的同步

在使用的時候,多線程最讓人頭疼的也許就是同步了。

如果你的線程只是完成一件並不需要訪問線程對象外部資源的工作,在這種情況下,線程互相之間不需要進行通信,此時Windows的運行性能最好。但是,線程很少能夠在所有的時間都獨立地進行操作。通常情況下,要生成一些線程來處理某個任務。當這個任務完成時,另一個線程必須了解這個情況。

系統中的所有線程都必須擁有對各種系統資源的訪問權,這些資源包括內存堆棧,文件,窗口和許多其他資源。如果一個線程需要獨占對資源的訪問權,那麽其他線程就無法完成它們的工作。反過來說,也不能讓任何一個線程在任何時間都能訪問所有的資源。如果在一個線程從內存塊中讀取數據時,另一個線程卻想要將數據寫入同一個內存塊,那麽這就像你在讀一本書時另一個人卻在修改書中的內容一樣。這樣,書中的內容就會被搞得亂七八糟,結果什麽也看不清楚。

線程需要在下面兩種情況下互相進行通信:

• 當有多個線程訪問共享資源而不使資源被破壞時。

• 當一個線程需要將某個任務已經完成的情況通知另外一個或多個線程時。

線程的同步包括許多方面的內容,Windows提供了許多方法,可以非常容易地實現線程的同步。但是,要想隨時了解一連串的線程想要做什麽,那是非常困難的。我們的頭腦的工作不是異步的,我們希望以一種有序的方式來思考許多事情,每次前進一步。不過多線程環境不是這樣運行的。你幾乎無法完全知道目標系統中存在多少線程,也不知道他們處在什麽狀態下,更不知道他們要幹什麽。

用戶方式下的線程同步

1、互鎖函數

在MSDN關于同步函數的幫助文檔中,你會看到大量的互鎖函數。他們大多以Interlocked****的名字存在。互鎖函數運行在用戶模式。它能保證當一個線程訪問一個變量時,其它線程無法訪問此變量,以確保變量值的唯一性。這種訪問方式被稱爲原子訪問。

常用的互鎖函數及其功能見如下列表:

函數

參數和功能

InterlockedIncrement

參數爲PLONG類型。此函數使一個LONG變量增1

InterlockedDecrement

參數爲PLONG類型。此函數使一個LONG變量減1

InterlockedExchangeAdd

參數1爲PLONG類型,參數2爲LONG類型。此函數將參數2賦給參數1指向的值

InterlockedExchange

參數1爲PLONG類型,參數2爲LONG類型。此函數將參數2的值賦給參數1指向的值

InterlockedExchangePointer

參數爲PVOID* 類型,參數2爲PVOID類型。此函數功能同上。

用InterlockedExchangeAdd來說明,他接受一個長整形變量的地址,然後將參數2的樹枝加到參數1制定的長整形數據上。我們前邊已經說了,他能保證當一個線程訪問此長整形變量時,其他線程無法訪問此變量,那麽他是如何實現的呢?答案取決于運行的是何種CPU平台。對于x86家族的CPU來說,互鎖函數會對總線發出一個硬件信號,防止另一個CPU訪問同一個內存地址。在Alpha平台上,互鎖函數能夠執行下列操作

1) 打開C P U中的一個特殊的位標志,並注明被訪問的內存地址。

2) 將內存的值讀入一個寄存器。

3) 修改該寄存器。

4) 如果C P U中的特殊位標志是關閉的,則轉入第二步。否則,特殊位標志仍然是打開的,寄存器的值重新存入內存。

互鎖函數工作與用戶模式之下,所以他的速度是非常快點的。有利就有弊,互鎖函數最大的缺點莫過于使用範圍的狹隘性了,它更多的只是對單個變量的保護。

2、臨界區

也有的地方叫它關鍵代碼段。臨界區指一個小代碼段,在代碼能夠執行前,它必須獨占對某些共享資源的訪問權。這是讓若幹行代碼能夠“以原子操作方式”來使用資源的一種方法。所謂原子操作方式,是指該代碼知道沒有別的線程要訪問該資源。簡單的說,就是一次只能由一個線程來執行的一段代碼。

先來看一段例子,在例子中,線程完成對一個數組初始化的工作,目標是在當前的數值上加1,TThread對象中還定義了一個FUseCritical的變量,它用來決定線程在執行時是否要使用臨界區的方式。

WIN32下DELPHI中的多線程【同步1】(四)
WIN32下DELPHI中的多線程【同步1】(四)
...{

WIN32下DELPHI中的多線程【同步1】(四)
作者:wudi_1982

WIN32下DELPHI中的多線程【同步1】(四)
聯系方式:wudi_1982@hotmail.com

WIN32下DELPHI中的多線程【同步1】(四)
本代碼爲演示代碼,只貼出了一些比較重要的代碼

WIN32下DELPHI中的多線程【同步1】(四)
轉載請著名出處

WIN32下DELPHI中的多線程【同步1】(四)
}

WIN32下DELPHI中的多線程【同步1】(四)

WIN32下DELPHI中的多線程【同步1】(四)
const

WIN32下DELPHI中的多線程【同步1】(四)
MaxArray = 1000;//公共內存區域的大小

WIN32下DELPHI中的多線程【同步1】(四)

WIN32下DELPHI中的多線程【同步1】(四)
//演示臨界區功能的線程類

WIN32下DELPHI中的多線程【同步1】(四)
type

WIN32下DELPHI中的多線程【同步1】(四)
TCriticalSectionThread= class(TThread)

WIN32下DELPHI中的多線程【同步1】(四)
private

WIN32下DELPHI中的多線程【同步1】(四)
FUseCritical : Boolean;//決定是否使用臨界區

WIN32下DELPHI中的多線程【同步1】(四)
procedure GetRestult;

WIN32下DELPHI中的多線程【同步1】(四)
protected

WIN32下DELPHI中的多線程【同步1】(四)
procedure Execute;override;

WIN32下DELPHI中的多線程【同步1】(四)
public

WIN32下DELPHI中的多線程【同步1】(四)
constructor Create(UseCritical : Boolean);

WIN32下DELPHI中的多線程【同步1】(四)
end;

WIN32下DELPHI中的多線程【同步1】(四)
end;

WIN32下DELPHI中的多線程【同步1】(四)

WIN32下DELPHI中的多線程【同步1】(四)
....

WIN32下DELPHI中的多線程【同步1】(四)

WIN32下DELPHI中的多線程【同步1】(四)
var

WIN32下DELPHI中的多線程【同步1】(四)
PublicMem : array[0..MaxArray] of integer;//一塊公共區域

WIN32下DELPHI中的多線程【同步1】(四)
Cs : TRTLCriticalSection;//描述臨界區信息的數據結構

WIN32下DELPHI中的多線程【同步1】(四)

WIN32下DELPHI中的多線程【同步1】(四)
實現代碼如下:

WIN32下DELPHI中的多線程【同步1】(四)
WIN32下DELPHI中的多線程【同步1】(四)
...{ TCriticalSectionThread }

WIN32下DELPHI中的多線程【同步1】(四)

WIN32下DELPHI中的多線程【同步1】(四)
constructor TCriticalSectionThread.Create(UseCritical: Boolean);

WIN32下DELPHI中的多線程【同步1】(四)
begin

WIN32下DELPHI中的多線程【同步1】(四)
//構造函數中接受是否使用臨界區的參數

WIN32下DELPHI中的多線程【同步1】(四)
FUseCritical := UseCritical;

WIN32下DELPHI中的多線程【同步1】(四)
inherited Create(false);

WIN32下DELPHI中的多線程【同步1】(四)
end;

WIN32下DELPHI中的多線程【同步1】(四)

WIN32下DELPHI中的多線程【同步1】(四)
procedure TCriticalSectionThread.Execute;

WIN32下DELPHI中的多線程【同步1】(四)
var

WIN32下DELPHI中的多線程【同步1】(四)
i : integer;

WIN32下DELPHI中的多線程【同步1】(四)
begin

WIN32下DELPHI中的多線程【同步1】(四)
inherited;

WIN32下DELPHI中的多線程【同步1】(四)
//運行完畢後自動釋放資源

WIN32下DELPHI中的多線程【同步1】(四)
FreeOnTerminate := True;

WIN32下DELPHI中的多線程【同步1】(四)
//如果使用了臨界區,則進入臨界區

WIN32下DELPHI中的多線程【同步1】(四)
if FUseCritical then

WIN32下DELPHI中的多線程【同步1】(四)
EnterCriticalSection(cs);

WIN32下DELPHI中的多線程【同步1】(四)
//對數組初始化

WIN32下DELPHI中的多線程【同步1】(四)
for i := 0 to MaxArray do

WIN32下DELPHI中的多線程【同步1】(四)
inc(PublicMem[i]);

WIN32下DELPHI中的多線程【同步1】(四)
Synchronize(GetRestult);

WIN32下DELPHI中的多線程【同步1】(四)
//離開

WIN32下DELPHI中的多線程【同步1】(四)
if FUseCritical then

WIN32下DELPHI中的多線程【同步1】(四)
LeaveCriticalSection(Cs);

WIN32下DELPHI中的多線程【同步1】(四)
end;

WIN32下DELPHI中的多線程【同步1】(四)

WIN32下DELPHI中的多線程【同步1】(四)
procedure TCriticalSectionThread.GetRestult;

WIN32下DELPHI中的多線程【同步1】(四)
var

WIN32下DELPHI中的多線程【同步1】(四)
i : integer;

WIN32下DELPHI中的多線程【同步1】(四)
begin

WIN32下DELPHI中的多線程【同步1】(四)
//將結果顯示在Form1.listbox1中

WIN32下DELPHI中的多線程【同步1】(四)
for i := 0 to MaxArray do

WIN32下DELPHI中的多線程【同步1】(四)
Form1.ListBox1.Items.Add(inttostr(PublicMem[i]));

WIN32下DELPHI中的多線程【同步1】(四)
end;

WIN32下DELPHI中的多線程【同步1】(四)

WIN32下DELPHI中的多線程【同步1】(四)
//調用這個線程類來演示臨界區功能的代碼

WIN32下DELPHI中的多線程【同步1】(四)
procedure TForm1.Button3Click(Sender: TObject);

WIN32下DELPHI中的多線程【同步1】(四)
begin

WIN32下DELPHI中的多線程【同步1】(四)
//將公共內存區域填充爲0

WIN32下DELPHI中的多線程【同步1】(四)
FillMemory(@PublicMem,sizeof(PublicMem),0);

WIN32下DELPHI中的多線程【同步1】(四)
ListBox1.Clear;

WIN32下DELPHI中的多線程【同步1】(四)
//一個CheckBox,用來接受是否使用臨界區方式的信息

WIN32下DELPHI中的多線程【同步1】(四)
if ckbxUsesC.Checked then

WIN32下DELPHI中的多線程【同步1】(四)
//如果使用臨界區,則首先初始化

WIN32下DELPHI中的多線程【同步1】(四)
InitializeCriticalSection(cs);

WIN32下DELPHI中的多線程【同步1】(四)
//同時生成兩個線程

WIN32下DELPHI中的多線程【同步1】(四)
TCriticalSectionThread.Create(ckbxUsesC.Checked);

WIN32下DELPHI中的多線程【同步1】(四)
TCriticalSectionThread.Create(ckbxUsesC.Checked);

WIN32下DELPHI中的多線程【同步1】(四)
end;

WIN32下DELPHI中的多線程【同步1】(四)

WIN32下DELPHI中的多線程【同步1】(四)
//好的編碼習慣中,你應該在確定線程已經不再需要使用臨界區時,用DeleteCriticalSection(cs);清楚這個結構

WIN32下DELPHI中的多線程【同步1】(四)

WIN32下DELPHI中的多線程【同步1】(四)

整理上面代碼,並執行,在MaxArray定義比較大的時候(也就是一個線程完成工作需要時間比較長的時候),你會發現當不使用臨界區時,兩個進程“同時”出現對公共內存區的訪問,數組沒有按照你的預定方式進行初始化(理想的情況是數組先被初始化爲1,然後再是2,可實際情況,你可能看到在第一個線程對數組初始化時,第二個線程搶占了CPU,所以,大量的數組成員被改寫成了2),當使用臨界區時,你會發現數組按照我們設定的思路先被初始化爲1,然後再初始化爲2。

讓我們來看看在使用臨界區的情況下系統是如何調度這兩個線程的,當第一個線程創建之後,它成爲可調度狀態,至于系統目前是否分配CPU時間片給它,我們不知道,這要看系統中其他進程的情況,然後第二個線程創建,並且也成爲可調度狀態,當系統發現可以調度這兩個線程的時候(也許在第一個線程創建之後,它就已經被調度了),系統調度一個線程,我們這裏暫且假設調度的是第一個線程,線程執行EnterCriticalSection(cs)更新CRITICAL_SECTION(DLEPHI中將它定義爲TRTLCriticalSection的記錄)的成員變量,以指明調用線程已被賦予訪問權並立即返回,使該線程能夠繼續運行,然後在一定時間之後,線程2搶占了CPU,然後執行EnterCriticalSection(cs),刷新CRITICAL_SECTION的成員變臉,系統發現一個線程已經被賦予了資源的訪問權,這時,系統將調用線程(我們這裏的線程2)置于等待狀態。這種情況是極好的,因爲等待的線程不會浪費任何CPU 時間。系統能夠記住該線程想要訪問該資源並且自動更新CRITICAL_SECTION的成員變量,直到線程1重新被調度,並執行了LeaveCriticalSection函數,這時系統便將線程2置爲可調度狀態,然後在合適的時間,線程2被調度,再次完成對數組初始化的工作。

使用臨界區時要注意的內容

1、EnterCriticalSection和LeaveCriticalSection要配對出現,如果你只調用EnterCriticalSection而忘記使用LeaveCriticalSection,那麽結果將是可怕的,這意味著其他需要訪問被保護資源的線程將永遠的等待下去,直到最終超時,産生一個異常條件。超時的時間被定義在注冊表的HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager中,默認大約時30天的時間。

2、CRITICAL_SECTION結構中的成員應該在任何線程試圖訪問被保護的資源之前初始化。你要調用InitializeCriticalSection來完成此操作。如果一個線程試圖進入未初始化的CRITICAL_SECTION,那麽結果將是無法預料的。

3、當知道進程的線程不再試圖訪問共享資源時,用DeleteCriticalSection函數清楚CRITICAL_SECTION結構

幾個有用的技巧

1)、每個需保護的共享資源使用一個單獨的CRITICAL_SECTION變量,例如線程1和線程2訪問一個資源A,而線程1和線程3訪問另一個資源B,那最好都資源A、B都使用一個單獨的CRITICAL_SECTION變量,如果使用同一個CRITICAL_SECTION變量,那麽加入線程1先被調度了,線程2、3則只有等待線程1完成對A、B的使用後才有機會運行。如果使用單獨的CRITICAL_SECTION變量,則在線程1使用完A之後,線程2即可迅速成爲被調度狀態。

2)、當要訪問多個被保護資源時,如果你使用的不同的CRITICAL_SECTION變量,那麽要注意他們的順序,例如線程1先後調用EnterCriticalSection(cs1);EnterCriticalSection(cs2),而有另外一個線程則使用另外的順序EnterCriticalSection(cs2),EnterCriticalSection(cs1),那麽將有可能出現死鎖的情況,線程1被調度,獲得被cs1保護的資源A的使用權,然後線程2被調度,獲得了被cs2保護的資源B的使用權,那麽此時,無論是線程1,還是線程2,都將因爲對方而一直等待下去。

3)、不要在臨界區內長時間執行那些不需要使用保護資源的代碼。如果長時間執行那些不必要保護的代碼,其他的希望訪問保護資源的線程將長時間的等待下去。

線程與內核對象的同步

用戶方式同步的優點是它的同步速度非常快。雖然用戶方式的線程同步機制具有速度快的優點,但其局限性也是明顯的。例如,互鎖函數家族只能在單值上運行,根本無法使線程進入等待狀態。我們使用臨界區的方式可以使得線程進入等待狀態,但是只能用這些代碼段對單個進程中的線程實施同步。因爲在等待進臨界區時你無法方便的設定超時值,所以有可能出現死鎖的狀態。

上面我一直強調了一個概念,就是等待狀態,這是很重要的,因爲等待狀態的線程將不使用CPU資源。

內核對象機制的適應性遠遠優于用戶方式機制。實際上,內核對象機制的唯一不足之處是它的速度比較慢。《WINDOWS核心編程》一書中說”這個轉換需要很大的代價:往返一次需要占用x86平台上的大約1000個CPU周期,當然,這還不包括執行內核方式代碼,即實現線程調用的函數的代碼所需的時間。“,我沒有測試過,不知道這1000個CPU周期的說法是否准確,但可以肯定內核對象機制將比用戶模式下的同步要慢。

Windows的內核對象,包括進程,線程和作業等。可以將所有這些內核對象用于同步目的。對于線程同步來說,這些內核對象中的每種對象都可以說是處于已通知或未通知的狀態之中。例如,進程內核對象總是在未通知狀態中創建的。當進程終止運行時,操作系統自動使該進程的內核對象處于已通知狀態。一旦進程內核對象得到通知,它將永遠保持這種狀態,它的狀態永遠不會改爲未通知狀態。當進程正在運行的時候,進程內核對象處于未通知狀態,當進程終止運行的時候,它就變爲已通知狀態。

實際上,線程內核對象也遵循同樣的規則。與進程內核對象一樣,線程內核對象也可以處于已通知狀態或未通知狀態。

下面的內核對象可以處于已通知狀態或未通知狀態:進程、文件修改通知線程、事件、作業、 可等待定時器、文件、 信標、控制台輸入、 互斥對象

線程可以使自己進入等待狀態,直到一個對象變爲已通知狀態。注意,用于控制每個對象的已通知/未通知狀態的規則要根據對象的類型而定。

等待函數

最常用的莫過于WaitForSingleObject

DWORD WaitForSingleObject(HANDLE hObject,DWORD dwMillseconds);

當線程調用該函數時,第一個參數標識一個能夠支持被通知/未通知的內核對象。第二個參數允許該線程指明爲了等待該對象變爲已通知狀態,它將等待多長時間。WaitForSingleObject的返回值能夠指明調用線程爲什麽再次變爲可調度狀態。如果線程等待的對象變爲已通知狀態,那麽返回值是WAIT_OBJECT_0。如果設置的超時已經到期,則返回值是WAIT_TIMEOUT。如果將一個錯誤的值(如一個無效句柄)傳遞給WaitForSingleObject,那麽返回值將是WAIT_FAILED。

事件對象

在所有的內核對象中,事件內核對象是個最基本的對象。它們包含一個使用計數(與所有內核對象一樣),一個用于指明該事件是個自動重置的事件還是一個人工重置的事件的布爾值,另一個用于指明該事件處于已通知狀態還是未通知狀態的布爾值。

事件能夠通知一個操作已經完成。有兩種不同類型的事件對象。一種是人工重置的事件,另一種是自動重置的事件。當人工重置的事件得到通知時,等待該事件的所有線程均變爲可調度線程。當一個自動重置的事件得到通知時,等待該事件的線程中只有一個線程變爲可調度線程。

當一個線程執行初始化操作,然後通知另一個線程執行剩余的操作時,事件使用得最多。事件初始化爲未通知狀態,然後,當該線程完成它的初始化操作後,它就將事件設置爲已通知狀態。這時,一直在等待該事件的另一個線程發現該事件已經得到通知,因此它就變成可調度線程。這第二個線程知道第一個線程已經完成了它的操作。

HANDLE CreateEvent(PSECURITY_ATTRIBUTS psa,

bool fManualRest,

bool fInitialState,

PCTSTR pszname);

第一個參數用來制定安全屬性,通常我們用null,最後一個參數用來制定一個名字。fManualRest,參數是個布爾值,它能夠告訴系統是創建一個人工重置的事件還是創建一個自動重置的事件(FALSE)。fInitialState,參數用于指明該事件是要初始化爲已通知狀態(TRUE)還是未通知狀態(FALSE)。當系統創建事件對象後,createEvent就將與進程相關的句柄返回給事件對象。其他進程中的線程可以獲得對該對象的訪問權,方法是使用在pszName參數中傳遞的相同值,使用繼承性,使用DuplicateHandle函數等來調用CreateEvent,或者調用OpenEvent,在pszName數中設定一個與調用CreateEvent時設定的名字相匹配的名字 。

一旦事件已經創建,就可以直接控制它的狀態。當調用SetEvent時,可以將事件改爲已通知狀態:當調用ResetEvent函數時,可以將該事件改爲未通知狀態。

自動重置的事件與人工重置事件的區別在于,自動重置事件在線程成功地等待到該對象時,自動重置的事件就會自動重置到未通知狀態。通常沒有必要爲自動重置的事件調用ResetEvent函數,因爲系統會自動對事件進行重置

一個例子:

WIN32下DELPHI中的多線程【同步1】(四)
WIN32下DELPHI中的多線程【同步1】(四)
...{

WIN32下DELPHI中的多線程【同步1】(四)
作者:wudi_1982

WIN32下DELPHI中的多線程【同步1】(四)
聯系方式:wudi_1982@hotmail.com

WIN32下DELPHI中的多線程【同步1】(四)
本代碼只貼出了一些比較關鍵的部分

WIN32下DELPHI中的多線程【同步1】(四)
轉載請著名出處

WIN32下DELPHI中的多線程【同步1】(四)
}

WIN32下DELPHI中的多線程【同步1】(四)

WIN32下DELPHI中的多線程【同步1】(四)
//利用事件對象演示同步的TThread派生類

WIN32下DELPHI中的多線程【同步1】(四)
TEventThread=class(TThread)

WIN32下DELPHI中的多線程【同步1】(四)
private

WIN32下DELPHI中的多線程【同步1】(四)
CurCount : integer;//當前計數

WIN32下DELPHI中的多線程【同步1】(四)
Flabel : TLabel;//顯示當前計數的Tlabel組建

WIN32下DELPHI中的多線程【同步1】(四)
procedure GetRestult;

WIN32下DELPHI中的多線程【同步1】(四)
protected

WIN32下DELPHI中的多線程【同步1】(四)
procedure Execute;override;

WIN32下DELPHI中的多線程【同步1】(四)
public

WIN32下DELPHI中的多線程【同步1】(四)
constructor Create(Alabel : TLabel);

WIN32下DELPHI中的多線程【同步1】(四)
end;

WIN32下DELPHI中的多線程【同步1】(四)

WIN32下DELPHI中的多線程【同步1】(四)
var

WIN32下DELPHI中的多線程【同步1】(四)
EventHandle : THandle;//事件對象的句柄

WIN32下DELPHI中的多線程【同步1】(四)

WIN32下DELPHI中的多線程【同步1】(四)
//TEventThread的實現代碼

WIN32下DELPHI中的多線程【同步1】(四)

WIN32下DELPHI中的多線程【同步1】(四)
WIN32下DELPHI中的多線程【同步1】(四)
...{ TEventThread }

WIN32下DELPHI中的多線程【同步1】(四)

WIN32下DELPHI中的多線程【同步1】(四)
constructor TEventThread.Create(Alabel: TLabel);

WIN32下DELPHI中的多線程【同步1】(四)
begin

WIN32下DELPHI中的多線程【同步1】(四)
Flabel := Alabel;

WIN32下DELPHI中的多線程【同步1】(四)
inherited Create(False);

WIN32下DELPHI中的多線程【同步1】(四)
end;

WIN32下DELPHI中的多線程【同步1】(四)

WIN32下DELPHI中的多線程【同步1】(四)
procedure TEventThread.Execute;

WIN32下DELPHI中的多線程【同步1】(四)
var

WIN32下DELPHI中的多線程【同步1】(四)
i : integer;

WIN32下DELPHI中的多線程【同步1】(四)
begin

WIN32下DELPHI中的多線程【同步1】(四)
inherited;

WIN32下DELPHI中的多線程【同步1】(四)
CurCount := 0;

WIN32下DELPHI中的多線程【同步1】(四)
for i := 0 to 10000 do

WIN32下DELPHI中的多線程【同步1】(四)
begin

WIN32下DELPHI中的多線程【同步1】(四)
case WaitForSingleObject(EventHandle,5000) of

WIN32下DELPHI中的多線程【同步1】(四)
WAIT_OBJECT_0 : begin//如果等待到事件對象,則將當前技術加1,並且顯示在指定label上

WIN32下DELPHI中的多線程【同步1】(四)
Inc(CurCount);

WIN32下DELPHI中的多線程【同步1】(四)
Synchronize(GetRestult);

WIN32下DELPHI中的多線程【同步1】(四)
Sleep(0);

WIN32下DELPHI中的多線程【同步1】(四)
//關于sleep,switchtoThread的使用前面已經說了

WIN32下DELPHI中的多線程【同步1】(四)
// SwitchToThread

WIN32下DELPHI中的多線程【同步1】(四)
//Application.ProcessMessages;

WIN32下DELPHI中的多線程【同步1】(四)
end;//WAIT_OBJECT_0

WIN32下DELPHI中的多線程【同步1】(四)
WAIT_TIMEOUT : begin//超時則自動重置當前計數

WIN32下DELPHI中的多線程【同步1】(四)
CurCount := 0;

WIN32下DELPHI中的多線程【同步1】(四)
Synchronize(GetRestult);

WIN32下DELPHI中的多線程【同步1】(四)
SwitchToThread;

WIN32下DELPHI中的多線程【同步1】(四)
end;

WIN32下DELPHI中的多線程【同步1】(四)
end;

WIN32下DELPHI中的多線程【同步1】(四)
end;

WIN32下DELPHI中的多線程【同步1】(四)
end;

WIN32下DELPHI中的多線程【同步1】(四)

WIN32下DELPHI中的多線程【同步1】(四)
procedure TEventThread.GetRestult;

WIN32下DELPHI中的多線程【同步1】(四)
begin

WIN32下DELPHI中的多線程【同步1】(四)
Flabel.Caption := IntToStr(CurCount);

WIN32下DELPHI中的多線程【同步1】(四)
end;

WIN32下DELPHI中的多線程【同步1】(四)

WIN32下DELPHI中的多線程【同步1】(四)
//窗體單元中測試TEventThread的一些代碼

WIN32下DELPHI中的多線程【同步1】(四)
procedure TForm1.CreateTClick(Sender: TObject);

WIN32下DELPHI中的多線程【同步1】(四)
begin

WIN32下DELPHI中的多線程【同步1】(四)
//這裏創建了TEventThread線程類的兩個實例,目的是更好的演示自動重置和手動重置的區別

WIN32下DELPHI中的多線程【同步1】(四)
TEventThread.Create(labEvent);

WIN32下DELPHI中的多線程【同步1】(四)
TEventThread.Create(labEvent2);

WIN32下DELPHI中的多線程【同步1】(四)
end;

WIN32下DELPHI中的多線程【同步1】(四)

WIN32下DELPHI中的多線程【同步1】(四)
procedure TForm1.CreateEClick(Sender: TObject);

WIN32下DELPHI中的多線程【同步1】(四)
begin

WIN32下DELPHI中的多線程【同步1】(四)
//生成一個事件對象,事件對象的重置方式以及初始化狀態通過兩個checkBox來決定

WIN32下DELPHI中的多線程【同步1】(四)
EventHandle := CreateEvent(nil,ckbxAutoReset.Checked, ckbxInitEventState.Checked,pchar('MyEvent'));

WIN32下DELPHI中的多線程【同步1】(四)
end;

WIN32下DELPHI中的多線程【同步1】(四)

WIN32下DELPHI中的多線程【同步1】(四)
procedure TForm1.SetEClick(Sender: TObject);

WIN32下DELPHI中的多線程【同步1】(四)
begin

WIN32下DELPHI中的多線程【同步1】(四)
//通知

WIN32下DELPHI中的多線程【同步1】(四)
SetEvent(EventHandle);

WIN32下DELPHI中的多線程【同步1】(四)
end;

WIN32下DELPHI中的多線程【同步1】(四)

WIN32下DELPHI中的多線程【同步1】(四)
procedure TForm1.ResetEClick(Sender: TObject);

WIN32下DELPHI中的多線程【同步1】(四)
begin

WIN32下DELPHI中的多線程【同步1】(四)
//未通知

WIN32下DELPHI中的多線程【同步1】(四)
ResetEvent(EventHandle);

WIN32下DELPHI中的多線程【同步1】(四)
end;

WIN32下DELPHI中的多線程【同步1】(四)

WIN32下DELPHI中的多線程【同步1】(四)
procedure TForm1.closeClick(Sender: TObject);

WIN32下DELPHI中的多線程【同步1】(四)
begin

WIN32下DELPHI中的多線程【同步1】(四)
//再你確定不需要此事件對象的時候,記得釋放資源

WIN32下DELPHI中的多線程【同步1】(四)
CloseHandle(EventHandle)

WIN32下DELPHI中的多線程【同步1】(四)
end;

WIN32下DELPHI中的多線程【同步1】(四)

WIN32下DELPHI中的多線程【同步1】(四)

WIN32下DELPHI中的多線程【同步1】(四)

利用事件對象演示同步的程序界面

WIN32下DELPHI中的多線程【同步1】(四)

整理上述代碼,然後分別用不同的配置不同順序的點擊按鈕,可以讓你對事件對象的使用加深了解。

內核方式的同步還有很多種,但原理基本都一樣,後續文章對盡可能的依次舉例列舉。

參考文獻:《WINDOWS核心編程》

注:轉載請著名出處,謝謝

 
WIN32下DELPHI中的多線程【同步1】(四)
線程的同步 在使用的時候,多線程最讓人頭疼的也許就是同步了。 如果你的線程只是完成一件並不需要訪問線程對象外部資源的工作,在這種情況下,線程互相之間不需要進行通信,此時Windows的運行性能最好。但是...查看完整版>>WIN32下DELPHI中的多線程【同步1】(四)
 
WIN32下DELPHI中的多線程【同步2】(五)
線程同步2 上一文中曾經介紹了線程同步的一些方法,其實完成同步還有很多很多的辦法,這裏最後介紹一種方式--信號量內核對象。並借此來回顧線程同步。 在談論信號量之前,我想先談論另外一種方式,一種你...查看完整版>>WIN32下DELPHI中的多線程【同步2】(五)
 
WIN32下DELPHI中的多線程【變量存儲】(三)
線程中的變量 由于每個線程都代表了一個不同的執行路徑,因此,最好有一種只限于一個線程內部使用的數據, 要實現上述目的有以下幾種方式: 1、局部變量(基于棧),很簡單,在你的線程函數中你定義的變量既...查看完整版>>WIN32下DELPHI中的多線程【變量存儲】(三)
 
WIN32下DELPHI中的多線程【線程的調度】(二)
線程的調度 每個線程是擁有一個上下文結構的,這個結構維護在線程的內核對象中。這個上下文結構反映了線程上次運行時該線程的C P U寄存器的狀態。每隔20ms左右,Windows要查看當前存在的所有線程內核對象。在...查看完整版>>WIN32下DELPHI中的多線程【線程的調度】(二)
 
Servlet及JSP中的多線程同步問題
Servlet及JSP中的多線程同步問題
  Servlet/jsp技術和asp、php等相比,由于其多線程運行而具有很高的執行效率。由于Servlet/JSP默認是以多線程模式執行的,所以,在編寫代碼時需要非常細致地考慮多線程的同步問題。然而,很多人編寫Servlet/JSP程序...查看完整版>>Servlet及JSP中的多線程同步問題