| 導購 | 订阅 | 在线投稿
分享
 
 
當前位置: 王朝網路 >> delphi >> WIN32下DELPHI中的多線程【同步2】(五)
 

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

   轉載請著名出處,謝謝!
 
 
 
上一篇《WIN32下DELPHI中的多線程【同步1】(四)》
下一篇《Delphi和C++的語法區別》
 
 
 
 
 
 
日版寵物情人插曲《Winding Road》歌詞

日版寵物情人2017的插曲,很帶節奏感,日語的,女生唱的。 最後聽見是在第8集的時候女主手割傷了,然後男主用嘴幫她吸了一下,插曲就出來了。 歌手:Def...

兄弟共妻,我成了他們夜裏的美食

老鍾家的兩個兒子很特別,就是跟其他的人不太一樣,魔一般的執著。兄弟倆都到了要結婚的年齡了,不管自家老爹怎麽磨破嘴皮子,兄弟倆說不娶就不娶,老父母爲兄弟兩操碎了心...

如何磨出破洞牛仔褲?牛仔褲怎麽剪破洞?

把牛仔褲磨出有線的破洞 1、具體工具就是磨腳石,下面墊一個硬物,然後用磨腳石一直磨一直磨,到把那塊磨薄了,用手撕開就好了。出來的洞啊很自然的。需要貓須的話調幾...

我就是掃描下圖得到了敬業福和愛國福

先來看下敬業福和愛國福 今年春節,支付寶再次推出了“五福紅包”活動,表示要“把欠大家的敬業福都還給大家”。 今天該活動正式啓動,和去年一樣,需要收集“五福”...

冰箱異味産生的原因和臭味去除的方法

有時候我們打開冰箱就會聞到一股異味,冰箱裏的這種異味是因爲一些物質發出的氣味的混合體,聞起來讓人惡心。 産生這些異味的主要原因有以下幾點。 1、很多人有這種習...

《極品家丁》1-31集大結局分集劇情介紹

簡介 《極品家丁》講述了現代白領林晚榮無意回到古代金陵,並追隨蕭二小姐化名“林三”進入蕭府,不料卻陰差陽錯上演了一出低級家丁拼搏上位的“林三升職記”。...

李溪芮《極品家丁》片尾曲《你就是我最愛的寶寶》歌詞

你就是我最愛的寶寶 - 李溪芮 (電視劇《極品家丁》片尾曲) 作詞:常馨內 作曲:常馨內 你的眉 又鬼馬的挑 你的嘴 又壞壞的笑 上一秒吵鬧 下...

烏梅的功效與作用以及烏梅的食用禁忌有哪些?

烏梅,又稱春梅,中醫認爲,烏梅味酸,性溫,無毒,具有安心、除熱、下氣、祛痰、止渴調中、殺蟲的功效,治肢體痛、肺痨病。烏梅泡水喝能治傷寒煩熱、止吐瀉,與幹姜一起制...

什麽是脂肪粒?如何消除臉部脂肪粒?

什麽是脂肪粒 在我們的臉上總會長一個個像脂肪的小顆粒,弄也弄不掉,而且顔色還是白白的。它既不是粉刺也不是其他的任何痘痘,它就是脂肪粒。 脂肪粒雖然也是由油脂...

網絡安全治理:國家安全保障的主要方向是打擊犯罪,而不是處置和懲罰受害者

來源:中國青年報 新的攻擊方法不斷湧現,黑客幾乎永遠占據網絡攻擊的上風,我們不可能通過技術手段杜絕網絡攻擊。國家安全保障的主要方向是打擊犯罪,而不是處置和懲罰...

河南夫妻在溫嶺網絡直播“造人”內容涉黃被刑事拘留

夫妻網絡直播“造人”爆紅   1月9日,溫嶺城北派出所接到南京警方的協查通告,他們近期打掉了一個涉黃直播APP平台。而根據掌握的線索,其中有一對涉案的夫妻主播...

如何防止牆紙老化?牆紙變舊變黃怎麽辦?

如何防止牆紙老化? (1)選擇透氣性好的牆紙 市場上牆紙的材質分無紡布的、木纖維的、PVC的、玻璃纖維基材的、布面的等,相對而言,PVC材質的牆紙最不透氣...

鮮肌之謎非日本生産VS鮮肌之謎假日貨是謠言

觀點一:破日本銷售量的“鮮肌之謎” 非日本生産 近一段時間,淘寶上架了一款名爲“鮮肌之謎的” 鲑魚卵巢美容液,號稱是最近日本的一款推出的全新護膚品,産品本身所...

中國最美古詩詞精選摘抄

系腰裙(北宋詞人 張先) 惜霜蟾照夜雲天,朦胧影、畫勾闌。人情縱似長情月,算一年年。又能得、幾番圓。 欲寄西江題葉字,流不到、五亭前。東池始有荷新綠,尚小如...

關于女人的經典語句

關于女人的經典語句1、【做一個獨立的女人】 思想獨立:有主見、有自己的人生觀、價值觀。有上進心,永遠不放棄自己的理想,做一份自己喜愛的事業,擁有快樂和成就...

未來我們可以和性愛機器人結婚嗎?

你想體驗機器人性愛嗎?你想和性愛機器人結婚嗎?如果你想,機器人有拒絕你的權利嗎? 近日,第二屆“國際人類-機器人性愛研討會”大會在倫敦金史密斯大學落下帷幕。而...

全球最變態的十個地方

10.土耳其地下洞穴城市 變態指數:★★☆☆☆ 這是土耳其卡帕多西亞的一個著名景點,傳說是當年基督教徒們爲了躲避戰爭而在此修建。裏面曾住著20000人,...

科學家稱,人類死亡後意識將在另外一個宇宙中繼續存活

據英國《每日快報》報道,一位科學家兼理論家Robert Lanza博士宣稱,世界上並不存在人類死亡,死亡的只是身體。他認爲我們的意識借助我們體內的能量生存,而且...

《屏裏狐》片頭曲《我愛狐狸精》歌詞是什麽?

《我愛狐狸精》 - 劉馨棋   (電視劇《屏裏狐》主題曲)   作詞:金十三&李旦   作曲:劉嘉   狐狸精 狐狸仙   千年修...

 
 
 
線程同步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 轉載請著名出處,謝謝!
󰈣󰈤
 
 
 
  免責聲明:本文僅代表作者個人觀點,與王朝網路無關。王朝網路登載此文出於傳遞更多信息之目的,並不意味著贊同其觀點或證實其描述,其原創性以及文中陳述文字和內容未經本站證實,對本文以及其中全部或者部分內容、文字的真實性、完整性、及時性本站不作任何保證或承諾,請讀者僅作參考,並請自行核實相關內容。
 
 
陽光靓麗的模特兒(8)
陽光靓麗的模特兒(7)
陽光靓麗的模特兒(6)
陽光靓麗的模特兒(5)
秋-印象
德慶盤龍峽 一
松江印象之三
雲之南(寬幅)
 
>>返回首頁<<
 
 
 
 熱帖排行
 
 
 
 
© 2005- 王朝網路 版權所有