| 導購 | 订阅 | 在线投稿
分享
 
 
 

WIN32下DELPHI中的多線程【同步2】(五)

來源:互聯網網民  2006-12-10 06:53:47  評論

線程同步2

上一文中曾經介紹了線程同步的一些方法,其實完成同步還有很多很多的辦法,這裏最後介紹一種方式--信號量內核對象。並借此來回顧線程同步。

在談論信號量之前,我想先談論另外一種方式,一種你最好不要使用的方式。假設你有一個公共內存區域,你不希望一個線程在完成一個操作之前另外一個線程對他進行另外的操作。抛開前面所有的知識,我們可以使用這樣一種辦法,一種所有人都會想到的辦法。

程序中設置一個布爾類型的公共變量FLAG,此公共變量唯一的最用是決定線程是否是否可以操作公共內存區域。如果是TRUE則允許操作,如果是FALSE則禁止操作。在線程將要執行對共享內存的操作時,反複判斷此變量,類似一個死循環,直到FLAG變爲TRUE。思路很簡單,實現起來也比前面介紹的那些方法更容易,在某種意義上說,它也是有效的。但文章前面曾經說過,最好不用使用這種方式,爲什麽?回顧線程的工作狀態,我們基本可以這樣劃分,

1、處于可調度狀態(挂起),此狀態下的線程正在等待CPU分配時間片給它來執行自己的操作

2、等待狀態,此時的線程我們可以稱它處在不可調度狀態,CPU絕不會在等待事件未發生之前分配時間片給它,例如一個線程正在等待某件事情的發生,就比如前邊說的等待事件內核對象的狀態變爲已通知

3、CPU已分配時間片給線程,它正在執行自己的操作。

假如我們使用事件內核對象來完成一些線程的同步,那麽前面曾經說過,當等待函數檢測到事件內核對象的狀態爲未通知狀態時,此線程將處于等待狀態,此時線程不會使用CPU,而如果使用前面介紹的那種反複判斷變量的方法,那麽此線程將占用CPU資源,這很重要,我始終認爲,對于一個合格的程序員而言,絕對不要無謂的浪費客戶的CPU資源。

雖然我說上面那種循環判斷公共狀態位的辦法不可取,但它卻反映了線程同步的思想,即使我們調用那些用于同步的API函數,事實上,同步的思想也是如此,只是實現的方法不同而已。

信號量

信號量內核對象用于對資源進行計數。它們與所有內核對象一樣,包含一個使用數量,但是它們也包含另外兩個帶符號的32位值,一個是最大資源數量,一個是當前資源數量。最大資源數量用于標識信標能夠控制的資源的最大數量,而當前資源數量則用于標識當前可以使用的資源的數量。

信號量的使用規則如下:

• 如果當前資源的數量大于0,則發出信標信號。

• 如果當前資源數量是0,則不發出信標信號。

• 系統決不允許當前資源的數量爲負值。

• 當前資源數量決不能大于最大資源數量。

創建一個信號量內核對象

HANDLE CreateSemaphore(

LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // pointer to security attributes

LONG lInitialCount, // initial count

LONG lMaximumCount, // maximum count

LPCTSTR lpName // pointer to semaphore-object name

);

和大多數創建內核對象的函數一樣,它的第一個參數用來接受安全信息,通常我們用NULL來表示默認,最後一個參數爲創建這個信號量的名字,此名字可以使得我們在其他的進程中使用此信號量,lInitialCount參數代表創建信號兩時允許資源訪問的個數,lMaximumCount用來指定最大資源數,不要讓lInitialCount大于lMaximumCount。

使用Create***創建內核對象時,要注意一個問題,例如,如果已經有一個進程A創建了一個名爲'wudi_1982'的信號量內核對象,當另外一個進程B也試圖創建名字爲'wudi_1982'的內核對象的時候,系統首先要查看是否已經存在一個名字爲'wudi_1982'的內核對象。由于確實存在一個帶有該名字的對象,因此內核要檢查對象的類型。如果類型相同(例如都是信號量內核對象),此時系統會執行一次安全檢查,以確定調用者是否擁有對該對象的完整的訪問權。如果擁有這種訪問權,系統就在進程B的句柄表中找出一個空項目,並對該項目進行初始化,使該項目指向現有的內核對象。如果該對象類型不匹配,或者調用者被拒絕訪問,那麽Create****將運行失敗(返回NULL)。

打開一個現有的信號量

HANDLE OpenSemaphore(

DWORD dwDesiredAccess, // access flag

BOOL bInheritHandle, // inherit flag

LPCTSTR lpName // pointer to semaphore-object name

);

參數dwDesiredAccess代表了訪問權限,bInheritHandle參數表明子進程是否可繼承,最後一個參數lpName 用于指明內核對象的名字。不能爲該參數傳遞NULL,必須傳遞以0結尾的地址。這些函數要搜索內核對象的單個名空間,以便找出匹配的空間。如果不存在帶有指定名字的內核對象,該函數返回NULL,GetLastError返回2(ERROR_FILE_NOT_FOUND)。但是,如果存在帶有指定名字的內核對象,並且它是相同類型的對象,那麽系統就要查看是否允許執行所需的訪問(通過dwDesiredAccess參數進行訪問)。如果擁有該訪問權,調用進程的句柄表就被更新,對象的使用計數被遞增。如果爲bInheritHandle,參數傳遞TRUE,那麽返回的句柄將是可繼承的。調用Create*函數與調用Open*函數之間的主要差別是,如果對象並不存在,那麽Create*函數將創建該對象,而Open*函數則運行失敗。

通過調用ReleaseSemaphore函數,線程就能夠對信標的當前資源數量進行遞增

BOOL ReleaseSemaphore(

HANDLE hSemaphore, // handle of the semaphore object

LONG lReleaseCount, // amount to add to current count

LPLONG lpPreviousCount // address of previous count

);

參數hSemaphore代表了要操作內核對象的句柄,lReleaseCount表明該函數此值添加給信標的當前資源數量,通常我們用1。lpPreviousCount返回當前資源數量的原始值,大多數的時候我們並不關心這個數值,所以一般賦值爲NULL。

一個例子:

WIN32下DELPHI中的多線程【同步2】(五)
WIN32下DELPHI中的多線程【同步2】(五)
...{
WIN32下DELPHI中的多線程【同步2】(五)
作者:wudi_1982
WIN32下DELPHI中的多線程【同步2】(五)
聯系方式:wudi_1982@hotmail.com
WIN32下DELPHI中的多線程【同步2】(五)
此代碼用來演示使用信號量完成線程的同步
WIN32下DELPHI中的多線程【同步2】(五)
轉載請著名出處
WIN32下DELPHI中的多線程【同步2】(五)
}
WIN32下DELPHI中的多線程【同步2】(五)

WIN32下DELPHI中的多線程【同步2】(五)
//主要代碼
WIN32下DELPHI中的多線程【同步2】(五)
const
WIN32下DELPHI中的多線程【同步2】(五)
SEMANAME='MySema';//信號量的名字
WIN32下DELPHI中的多線程【同步2】(五)
//線程類聲明
WIN32下DELPHI中的多線程【同步2】(五)
TSemaThread=class(TThread)
WIN32下DELPHI中的多線程【同步2】(五)
private
WIN32下DELPHI中的多線程【同步2】(五)
CurCount : integer;//當前計數
WIN32下DELPHI中的多線程【同步2】(五)
Flabel : TLabel;//一個用來在界面上顯示當前計數的label
WIN32下DELPHI中的多線程【同步2】(五)
procedure GetRestult;
WIN32下DELPHI中的多線程【同步2】(五)
protected
WIN32下DELPHI中的多線程【同步2】(五)
procedure Execute;override;
WIN32下DELPHI中的多線程【同步2】(五)
public
WIN32下DELPHI中的多線程【同步2】(五)
constructor Create(Alabel : TLabel);
WIN32下DELPHI中的多線程【同步2】(五)
end;
WIN32下DELPHI中的多線程【同步2】(五)

WIN32下DELPHI中的多線程【同步2】(五)
//線程類的實現代碼
WIN32下DELPHI中的多線程【同步2】(五)
constructor TSemaThread.Create(Alabel: TLabel);
WIN32下DELPHI中的多線程【同步2】(五)
begin
WIN32下DELPHI中的多線程【同步2】(五)
Flabel := Alabel;
WIN32下DELPHI中的多線程【同步2】(五)
inherited Create(False);
WIN32下DELPHI中的多線程【同步2】(五)
end;
WIN32下DELPHI中的多線程【同步2】(五)

WIN32下DELPHI中的多線程【同步2】(五)
procedure TSemaThread.Execute;
WIN32下DELPHI中的多線程【同步2】(五)
//注意下面這個常量的定義
WIN32下DELPHI中的多線程【同步2】(五)
const
WIN32下DELPHI中的多線程【同步2】(五)
SEMAPHORE_ALL_ACCESS=$1F0003;
WIN32下DELPHI中的多線程【同步2】(五)
var
WIN32下DELPHI中的多線程【同步2】(五)
i : integer;
WIN32下DELPHI中的多線程【同步2】(五)
SmHandle : THandle;
WIN32下DELPHI中的多線程【同步2】(五)
begin
WIN32下DELPHI中的多線程【同步2】(五)
inherited;
WIN32下DELPHI中的多線程【同步2】(五)
CurCount := 0;
WIN32下DELPHI中的多線程【同步2】(五)
SmHandle := OpenSemaphore(SEMAPHORE_ALL_ACCESS,false,SEMANAME);
WIN32下DELPHI中的多線程【同步2】(五)
WaitForSingleObject(SmHandle,INFINITE);
WIN32下DELPHI中的多線程【同步2】(五)
for i := 0 to 10000 do
WIN32下DELPHI中的多線程【同步2】(五)
begin
WIN32下DELPHI中的多線程【同步2】(五)
Inc(CurCount);
WIN32下DELPHI中的多線程【同步2】(五)
GetRestult;
WIN32下DELPHI中的多線程【同步2】(五)
end;
WIN32下DELPHI中的多線程【同步2】(五)
ReleaseSemaphore(SmHandle,1,nil);
WIN32下DELPHI中的多線程【同步2】(五)
CloseHandle(SmHandle);
WIN32下DELPHI中的多線程【同步2】(五)
end;
WIN32下DELPHI中的多線程【同步2】(五)

WIN32下DELPHI中的多線程【同步2】(五)
//調用此測試類的代碼
WIN32下DELPHI中的多線程【同步2】(五)
procedure TSemaThread.GetRestult;
WIN32下DELPHI中的多線程【同步2】(五)
begin
WIN32下DELPHI中的多線程【同步2】(五)
Flabel.Caption := IntToStr(CurCount);
WIN32下DELPHI中的多線程【同步2】(五)
end;
WIN32下DELPHI中的多線程【同步2】(五)

WIN32下DELPHI中的多線程【同步2】(五)
procedure TForm1.createTsClick(Sender: TObject);
WIN32下DELPHI中的多線程【同步2】(五)
begin
WIN32下DELPHI中的多線程【同步2】(五)
TSemaThread.Create(labSem);
WIN32下DELPHI中的多線程【同步2】(五)
TSemaThread.Create(labSem2);
WIN32下DELPHI中的多線程【同步2】(五)
TSemaThread.Create(labSem3);
WIN32下DELPHI中的多線程【同步2】(五)
end;
WIN32下DELPHI中的多線程【同步2】(五)

WIN32下DELPHI中的多線程【同步2】(五)
procedure TForm1.CreateSemClick(Sender: TObject);
WIN32下DELPHI中的多線程【同步2】(五)
begin
WIN32下DELPHI中的多線程【同步2】(五)
SmeHandle :=
WIN32下DELPHI中的多線程【同步2】(五)
CreateSemaphore(nil,1,3,SEMANAME);
WIN32下DELPHI中的多線程【同步2】(五)
end;
WIN32下DELPHI中的多線程【同步2】(五)

WIN32下DELPHI中的多線程【同步2】(五)
procedure TForm1.Button13Click(Sender: TObject);
WIN32下DELPHI中的多線程【同步2】(五)
begin
WIN32下DELPHI中的多線程【同步2】(五)
CloseHandle(SmeHandle)
WIN32下DELPHI中的多線程【同步2】(五)
end;
WIN32下DELPHI中的多線程【同步2】(五)

WIN32下DELPHI中的多線程【同步2】(五)

對上述例子操作的說明:

首先程序通過一個按鈕來生成一個信號量內核對象,當前使用計數是1,最大爲3,這裏,如果你有興趣,你可以將當前使用計數改爲2,從而你可以觀察到信號量內核對象和其他內核對象(例如互斥)的最大區別。當信號量已經生成之後,點擊按鈕創建三個線程,線程根據信號量的名字通過OpenSemaphore來打開,這樣做的一個好處是,你可以同時多次執行此程序,例如你將這個程序同時打開了3個,在其中一個中,首先設置信號量,然後讓其他的程序都執行線程操作,你會發現,他們依然同步的很好。這是臨界區無法做到的。

對代碼的一些說明:

1、多個進程之間來完成同步。在前面的例子中,我都是使用一個全軍變量***:Thandle來記錄內核對象,以使得我們可以在多個線程中訪問同一個內核對象,這裏,我沒有再使用這個辦法,而是利用了名字,只所以要這麽做,是因爲,如果你給內核對象起一個名字,那麽你可以方便的在其他線程中使用同一個內核對象。這也是使用內核對象完成同步和使用臨界區方式最大的不同,使用臨界區,你只能在同一個進程中來完成同步。你完成可以將上述代碼整理之後做成一個程序,然後同時執行多個此程序,來觀察效果。即使不再同一個進程之中,線程依然可以很好的完成同步。

2、Access Mask Format.在利用名字使用內核對象時,我們用到OpenSemaphore來完成操作,前面說了,它的第一個參數用來決定訪問權限,事實上,其他的內核對象,例如互斥,他們的open*操作都是如此。這個用來決定權限的參數至關重要,在上面的代碼中,我定義了一個常量const SEMAPHORE_ALL_ACCESS=$1F0003;,如果你在DLEPHI中使用過互斥對象來完成同步,你會覺得不可理解,因爲在使OpenMutex函數打開互斥對象時,第一個參數你可以直接使用MUTEX_ALL_ACCESS,那時因爲DELPHI的windows單元中存在對它的定義,看MSDN的幫助文檔,你會發現使用信號量時候,也有一個類似的已經定義的常量SEMAPHORE_ALL_ACCESS ,不過很可惜,DELPHI中並沒有定義這個常量,所以我們不得不自己定義。另外只得注意的一點是,通常情況下,我們都是使用$1F0003,但有時候你不得不使用其他的權限信息,此時,你必須注意的一點是,你要讓權限中包含SEMAPHORE_MODIFY_STATE(0x0002)這個信息,在MSDN中,它的說明如下,Modify state access, which is required for the ReleaseSemaphore function.你可以做將我上面的代碼進行簡單修來來測試,例如你將SEMAPHORE_ALL_ACCESS定義爲STANDARD_RIGHTS_REQUIRED or SYNCHRONIZE,那麽在程序執行的時候,你會發現只有第一個獲得CPU調度的線程可以正常完成操作,而其他等待此信號量的線程將永遠的等待下去,原因很簡單,就是剛才貼出的MSDN上的那一句話。根據那一句話,如果你將SEMAPHORE_ALL_ACCESS 定義爲$1F0002,你會發現,程序也沒有問題。爲什麽,這就要說到OpenSemaphore的dwDesiredAccess參數,那麽你就要了解Access Mask Format,可以根據下面的圖來加深理解。具體參考MSDN的幫助

WIN32下DELPHI中的多線程【同步2】(五)

3、內核對象的使用計數。在線程的執行代碼中,你可以看到在線程工作完成之後的CloseHandle(SmHandle)這一句,請記住,及時釋放不必要的資源,是一個很好的習慣。此時,你可能會問,我的第一個線程調用了CloseHandle(SmHandle),那麽我後邊還沒有執行的線程同樣需要這個資源,是否就不能執行了呢?答案是否定的。內核對象包含了一個使用計數信息,當你Create*的時候,使用計數是1,隨後,當open*的時候,使用計數加1。當你調用了一個CloseHandle時,在CloseHandle返回之前,它會清除進程的句柄表中的項目,該句柄現在對你的進程已經無效,不應該試圖使用它。無論內核對象是否已經撤消,都會發生清除操作。當調用CloseHandle函數之後,將不再擁有對內核對象的訪問權,不過,如果該對象的使用計數沒有遞減爲0,那麽該對象尚未被撤消。這沒有問題,它只是意味著一個或多個其他進程正在使用該對象。當其他進程停止使用該對象時(通過調用CloseHandle),該對象將被撤消。

一些可以用于同步的其他內核對象

互斥對象

CreateMutex、ReleaseMutex、openMutex

等待計時器對象

CreateWaitableTimer、SetWaitableTimer

轉載請著名出處,謝謝!

 
特别声明:以上内容(如有图片或视频亦包括在内)为网络用户发布,本站仅提供信息存储服务。
 
線程同步2 上一文中曾經介紹了線程同步的一些方法,其實完成同步還有很多很多的辦法,這裏最後介紹一種方式--信號量內核對象。並借此來回顧線程同步。 在談論信號量之前,我想先談論另外一種方式,一種你最好不要使用的方式。假設你有一個公共內存區域,你不希望一個線程在完成一個操作之前另外一個線程對他進行另外的操作。抛開前面所有的知識,我們可以使用這樣一種辦法,一種所有人都會想到的辦法。 程序中設置一個布爾類型的公共變量FLAG,此公共變量唯一的最用是決定線程是否是否可以操作公共內存區域。如果是TRUE則允許操作,如果是FALSE則禁止操作。在線程將要執行對共享內存的操作時,反複判斷此變量,類似一個死循環,直到FLAG變爲TRUE。思路很簡單,實現起來也比前面介紹的那些方法更容易,在某種意義上說,它也是有效的。但文章前面曾經說過,最好不用使用這種方式,爲什麽?回顧線程的工作狀態,我們基本可以這樣劃分, 1、處于可調度狀態(挂起),此狀態下的線程正在等待CPU分配時間片給它來執行自己的操作 2、等待狀態,此時的線程我們可以稱它處在不可調度狀態,CPU絕不會在等待事件未發生之前分配時間片給它,例如一個線程正在等待某件事情的發生,就比如前邊說的等待事件內核對象的狀態變爲已通知 3、CPU已分配時間片給線程,它正在執行自己的操作。 假如我們使用事件內核對象來完成一些線程的同步,那麽前面曾經說過,當等待函數檢測到事件內核對象的狀態爲未通知狀態時,此線程將處于等待狀態,此時線程不會使用CPU,而如果使用前面介紹的那種反複判斷變量的方法,那麽此線程將占用CPU資源,這很重要,我始終認爲,對于一個合格的程序員而言,絕對不要無謂的浪費客戶的CPU資源。 雖然我說上面那種循環判斷公共狀態位的辦法不可取,但它卻反映了線程同步的思想,即使我們調用那些用于同步的API函數,事實上,同步的思想也是如此,只是實現的方法不同而已。 信號量 信號量內核對象用于對資源進行計數。它們與所有內核對象一樣,包含一個使用數量,但是它們也包含另外兩個帶符號的32位值,一個是最大資源數量,一個是當前資源數量。最大資源數量用于標識信標能夠控制的資源的最大數量,而當前資源數量則用于標識當前可以使用的資源的數量。 信號量的使用規則如下: • 如果當前資源的數量大于0,則發出信標信號。 • 如果當前資源數量是0,則不發出信標信號。 • 系統決不允許當前資源的數量爲負值。 • 當前資源數量決不能大于最大資源數量。 創建一個信號量內核對象 HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // pointer to security attributes LONG lInitialCount, // initial count LONG lMaximumCount, // maximum count LPCTSTR lpName // pointer to semaphore-object name ); 和大多數創建內核對象的函數一樣,它的第一個參數用來接受安全信息,通常我們用NULL來表示默認,最後一個參數爲創建這個信號量的名字,此名字可以使得我們在其他的進程中使用此信號量,lInitialCount參數代表創建信號兩時允許資源訪問的個數,lMaximumCount用來指定最大資源數,不要讓lInitialCount大于lMaximumCount。 使用Create***創建內核對象時,要注意一個問題,例如,如果已經有一個進程A創建了一個名爲'wudi_1982'的信號量內核對象,當另外一個進程B也試圖創建名字爲'wudi_1982'的內核對象的時候,系統首先要查看是否已經存在一個名字爲'wudi_1982'的內核對象。由于確實存在一個帶有該名字的對象,因此內核要檢查對象的類型。如果類型相同(例如都是信號量內核對象),此時系統會執行一次安全檢查,以確定調用者是否擁有對該對象的完整的訪問權。如果擁有這種訪問權,系統就在進程B的句柄表中找出一個空項目,並對該項目進行初始化,使該項目指向現有的內核對象。如果該對象類型不匹配,或者調用者被拒絕訪問,那麽Create****將運行失敗(返回NULL)。 打開一個現有的信號量 HANDLE OpenSemaphore( DWORD dwDesiredAccess, // access flag BOOL bInheritHandle, // inherit flag LPCTSTR lpName // pointer to semaphore-object name ); 參數dwDesiredAccess代表了訪問權限,bInheritHandle參數表明子進程是否可繼承,最後一個參數lpName 用于指明內核對象的名字。不能爲該參數傳遞NULL,必須傳遞以0結尾的地址。這些函數要搜索內核對象的單個名空間,以便找出匹配的空間。如果不存在帶有指定名字的內核對象,該函數返回NULL,GetLastError返回2(ERROR_FILE_NOT_FOUND)。但是,如果存在帶有指定名字的內核對象,並且它是相同類型的對象,那麽系統就要查看是否允許執行所需的訪問(通過dwDesiredAccess參數進行訪問)。如果擁有該訪問權,調用進程的句柄表就被更新,對象的使用計數被遞增。如果爲bInheritHandle,參數傳遞TRUE,那麽返回的句柄將是可繼承的。調用Create*函數與調用Open*函數之間的主要差別是,如果對象並不存在,那麽Create*函數將創建該對象,而Open*函數則運行失敗。 通過調用ReleaseSemaphore函數,線程就能夠對信標的當前資源數量進行遞增 BOOL ReleaseSemaphore( HANDLE hSemaphore, // handle of the semaphore object LONG lReleaseCount, // amount to add to current count LPLONG lpPreviousCount // address of previous count ); 參數hSemaphore代表了要操作內核對象的句柄,lReleaseCount表明該函數此值添加給信標的當前資源數量,通常我們用1。lpPreviousCount返回當前資源數量的原始值,大多數的時候我們並不關心這個數值,所以一般賦值爲NULL。 一個例子: [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedBlockStart.gif[/img][/url][url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ContractedBlock.gif[/img][/url]...{ [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif[/img][/url] 作者:wudi_1982 [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif[/img][/url] 聯系方式:wudi_1982@hotmail.com [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif[/img][/url] 此代碼用來演示使用信號量完成線程的同步 [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif[/img][/url] 轉載請著名出處 [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedBlockEnd.gif[/img][/url]} [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url]//主要代碼 [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url]const [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] SEMANAME='MySema';//信號量的名字 [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] //線程類聲明 [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] TSemaThread=class(TThread) [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] private [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] CurCount : integer;//當前計數 [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] Flabel : TLabel;//一個用來在界面上顯示當前計數的label [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] procedure GetRestult; [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] protected [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] procedure Execute;override; [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] public [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] constructor Create(Alabel : TLabel); [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] end; [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url]//線程類的實現代碼 [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url]constructor TSemaThread.Create(Alabel: TLabel); [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url]begin [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] Flabel := Alabel; [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] inherited Create(False); [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url]end; [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url]procedure TSemaThread.Execute; [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url]//注意下面這個常量的定義 [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url]const [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] SEMAPHORE_ALL_ACCESS=$1F0003; [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url]var [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] i : integer; [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] SmHandle : THandle; [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url]begin [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] inherited; [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] CurCount := 0; [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] SmHandle := OpenSemaphore(SEMAPHORE_ALL_ACCESS,false,SEMANAME); [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] WaitForSingleObject(SmHandle,INFINITE); [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] for i := 0 to 10000 do [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] begin [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] Inc(CurCount); [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] GetRestult; [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] end; [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] ReleaseSemaphore(SmHandle,1,nil); [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] CloseHandle(SmHandle); [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url]end; [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url]//調用此測試類的代碼 [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url]procedure TSemaThread.GetRestult; [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url]begin [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] Flabel.Caption := IntToStr(CurCount); [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url]end; [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url]procedure TForm1.createTsClick(Sender: TObject); [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url]begin [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] TSemaThread.Create(labSem); [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] TSemaThread.Create(labSem2); [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] TSemaThread.Create(labSem3); [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url]end; [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url]procedure TForm1.CreateSemClick(Sender: TObject); [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url]begin [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] SmeHandle := [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] CreateSemaphore(nil,1,3,SEMANAME); [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url]end; [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url]procedure TForm1.Button13Click(Sender: TObject); [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url]begin [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] CloseHandle(SmeHandle) [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url]end; [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] [url=/bbs/detail_565954.html][img]http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif[/img][/url] 對上述例子操作的說明: 首先程序通過一個按鈕來生成一個信號量內核對象,當前使用計數是1,最大爲3,這裏,如果你有興趣,你可以將當前使用計數改爲2,從而你可以觀察到信號量內核對象和其他內核對象(例如互斥)的最大區別。當信號量已經生成之後,點擊按鈕創建三個線程,線程根據信號量的名字通過OpenSemaphore來打開,這樣做的一個好處是,你可以同時多次執行此程序,例如你將這個程序同時打開了3個,在其中一個中,首先設置信號量,然後讓其他的程序都執行線程操作,你會發現,他們依然同步的很好。這是臨界區無法做到的。 對代碼的一些說明: 1、多個進程之間來完成同步。在前面的例子中,我都是使用一個全軍變量***:Thandle來記錄內核對象,以使得我們可以在多個線程中訪問同一個內核對象,這裏,我沒有再使用這個辦法,而是利用了名字,只所以要這麽做,是因爲,如果你給內核對象起一個名字,那麽你可以方便的在其他線程中使用同一個內核對象。這也是使用內核對象完成同步和使用臨界區方式最大的不同,使用臨界區,你只能在同一個進程中來完成同步。你完成可以將上述代碼整理之後做成一個程序,然後同時執行多個此程序,來觀察效果。即使不再同一個進程之中,線程依然可以很好的完成同步。 2、Access Mask Format.在利用名字使用內核對象時,我們用到OpenSemaphore來完成操作,前面說了,它的第一個參數用來決定訪問權限,事實上,其他的內核對象,例如互斥,他們的open*操作都是如此。這個用來決定權限的參數至關重要,在上面的代碼中,我定義了一個常量const SEMAPHORE_ALL_ACCESS=$1F0003;,如果你在DLEPHI中使用過互斥對象來完成同步,你會覺得不可理解,因爲在使OpenMutex函數打開互斥對象時,第一個參數你可以直接使用MUTEX_ALL_ACCESS,那時因爲DELPHI的windows單元中存在對它的定義,看MSDN的幫助文檔,你會發現使用信號量時候,也有一個類似的已經定義的常量SEMAPHORE_ALL_ACCESS ,不過很可惜,DELPHI中並沒有定義這個常量,所以我們不得不自己定義。另外只得注意的一點是,通常情況下,我們都是使用$1F0003,但有時候你不得不使用其他的權限信息,此時,你必須注意的一點是,你要讓權限中包含SEMAPHORE_MODIFY_STATE(0x0002)這個信息,在MSDN中,它的說明如下,Modify state access, which is required for the ReleaseSemaphore function.你可以做將我上面的代碼進行簡單修來來測試,例如你將SEMAPHORE_ALL_ACCESS定義爲STANDARD_RIGHTS_REQUIRED or SYNCHRONIZE,那麽在程序執行的時候,你會發現只有第一個獲得CPU調度的線程可以正常完成操作,而其他等待此信號量的線程將永遠的等待下去,原因很簡單,就是剛才貼出的MSDN上的那一句話。根據那一句話,如果你將SEMAPHORE_ALL_ACCESS 定義爲$1F0002,你會發現,程序也沒有問題。爲什麽,這就要說到OpenSemaphore的dwDesiredAccess參數,那麽你就要了解Access Mask Format,可以根據下面的圖來加深理解。具體參考MSDN的幫助 [url=/bbs/detail_565954.html][img]http://p.blog.csdn.net/images/p_blog_csdn_net/wudi_1982/accctrl4.gif[/img][/url] 3、內核對象的使用計數。在線程的執行代碼中,你可以看到在線程工作完成之後的CloseHandle(SmHandle)這一句,請記住,及時釋放不必要的資源,是一個很好的習慣。此時,你可能會問,我的第一個線程調用了CloseHandle(SmHandle),那麽我後邊還沒有執行的線程同樣需要這個資源,是否就不能執行了呢?答案是否定的。內核對象包含了一個使用計數信息,當你Create*的時候,使用計數是1,隨後,當open*的時候,使用計數加1。當你調用了一個CloseHandle時,在CloseHandle返回之前,它會清除進程的句柄表中的項目,該句柄現在對你的進程已經無效,不應該試圖使用它。無論內核對象是否已經撤消,都會發生清除操作。當調用CloseHandle函數之後,將不再擁有對內核對象的訪問權,不過,如果該對象的使用計數沒有遞減爲0,那麽該對象尚未被撤消。這沒有問題,它只是意味著一個或多個其他進程正在使用該對象。當其他進程停止使用該對象時(通過調用CloseHandle),該對象將被撤消。 一些可以用于同步的其他內核對象 互斥對象 CreateMutex、ReleaseMutex、openMutex 等待計時器對象 CreateWaitableTimer、SetWaitableTimer 轉載請著名出處,謝謝!
󰈣󰈤
王朝萬家燈火計劃
期待原創作者加盟
 
 
 
>>返回首頁<<
 
 
 
 
 
 熱帖排行
 
 
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有