7.等待定時器核心對象
等待定時器核心對象是在某個時間或某個規定的時間間隔內發出自己的訊號通知核心對象。它們通常是用在:在某人時間執行某個操作。
建立定時器核心對象:
HANDLE CreateWaitableTimer(
PSECURITY_ATTRIBUTES psa,
BOOL fManualReset, PCTSTR pszName);
關於參數以前都介紹過了,在此不述。
建立定時器後,定時器是在未通知狀態,注意,定時器總是在未通知狀態下被建立,完畢之後,需要設定何時讓該對象變為已通知狀態,這就需要用到:SetWaitableTimer函數:
BOOL SetWaitableTimer(
HANDLE hTimer,
const LARGE_INTEGER *pDueTime,
LONG lPeriod,
PTIMERAPCROUTINE pfnCompletionRoutine,
PVOID pvArgToCompletionRoutine,
BOOL fResume);
其中,hTimer是指定時器核心對象的控制代碼。pDueTime與lPeriod是一起使用,pDueTimer表示的是定時器何時第一次報時,lPeriod表示是隔多長時間報時一次。至於詳細的參數設定與例子,書上講得比較清楚。
8.信標核心對象
信標核心對象是用來對資源進行計數的一個核心對象。它除了包含一個核心對象所共有的psa屬性以外,還有兩個帶符號的32位值的屬性,一個是最大資源數量,一個是當前資源數量。最大資源數量用於標識信標能夠控制的資源的最大數量,而當前資源數量則用於標識當前可以使用的資源的數量。
何時能使用信標核心對象,比如,我開發一個伺服器處理序,在這個進程中,我分配了五個可以供客戶使用的緩衝區,這樣伺服器能處理5個客戶機的請求。開始時,沒有客戶機請求,那麼,最大資源數量為5,當前資源數量為0。現在,有三個客戶請求緩衝區,伺服器處理序可以分配三個緩衝區為這三個客戶使用,伺服器建立三個線程,並使這三個線程處於可調度狀態。
但是,書上有一段話不太明白,就是:
當客戶機的請求被接受時,當前資源數量就遞增,當客戶機的請求被提交給伺服器的線程池時,當前資源數量就遞減。
究竟被接受與提交給伺服器線程池有什麼區別呢?為什麼會導致一會兒又遞增,一會兒又遞減?
書上還指出了信標的使用規則:
• 如果當前資源的數量大於0,則發出信標訊號。
• 如果當前資源數量是0,則不發出信標訊號。
• 系統決不允許當前資源的數量為負值。
• 當前資源數量決不能大於最大資源數量。
還有:不要將信標核心對象的使用數量與當前資源數量混為一談。
建立信標核心對象:
HANDLE CreateSemaphore(
PSECURITY_ATTRIBUTE psa,
LONG lInitialCount,
LONG lMaximumCount, PCTSTR pszName);
通過調用ReleaseSemaphore函數,可以對當前資源數量進行遞增:
BOOL ReleaseSemaphore(
HANDLE hsem,
LONG lReleaseCount, PLONG plPreviousCount);
9.互斥核心對象
互斥核心對象能夠保證線程擁有對單個對象的訪問權。互斥對象包含一個使用計數,一個線程ID,一個遞迴計數器。他的功能與關鍵程式碼片段相同。不同的是一個屬於核心對象,一個屬於使用者方式對象,另外一個不同的是互斥核心對象可以讓不同的進程中的線程訪問同一資源。
互斥對象的使用規則:
• 如果線程I D是0(這是個無效I D),互斥對象不被任何線程所擁有,並且發出該互斥對象的通知訊號。
• 如果I D是個非0數字,那麼一個線程就擁有互斥對象,並且不發出該互斥對象的通知訊號。
• 與所有其他核心對象不同, 互斥對象在作業系統中擁有特殊的代碼,允許它們違反正常的規則(後面將要介紹這個異常情況)。
關於互斥對象的函數:
建立互斥對象:
HANDLE CreateMutex(
PSECURITY_ATTRIBUTES psa,
BOOL fInitialOwner,
PCTSTR pszName);
開啟互斥對象:
HANDLE OpenMutex(
DWORD fdwAccess,
BOOL bInheritHandle,
PCTSTR pszName);
fInitialOwner控制著互斥對象的初始狀態,如果傳遞FALSE,則表示互斥對象的線程ID和遞迴計數均被設為0. 它意味著資源不被任何線程所佔有,因此它要發出通知訊號。
如果傳遞TRUE,那麼對象的線程ID被設定成調用它線程的ID,遞迴計數設為1,資源被本線程佔有,不發出通知訊號。
一旦線程成功等待到一個互斥對象,它就已經擁有了對資源的獨佔訪問權,任何要訪問該資源的線程均被置於等待中。
當一個線程不需要對該資源的訪問權時,必須釋放互斥對象:
BOOL ReleaseMutex(HANDLE hMutex);
互斥對象不同於其它對象的地方:
因為它有線程所有權的概念。沒有哪個對象能夠記住哪一個線程成功的等待到該對象,只有互斥對象能夠保持跟蹤。
書中列了兩張表,一張是互斥對象與關鍵代碼的區別,一張是線程同步對象速查表。比較有用,COPY至此:
表9-1 互斥對象與關鍵程式碼片段的比較
特性 |
互斥對象 |
關鍵程式碼片段 |
運行速度 |
慢 |
快 |
是否能夠跨進程邊界來使用 |
是 |
否 |
聲明 |
HANDLE hmtx; |
CRITICAL_SECTION cs; |
初始化 |
h m t x = C r e a t e M u t e x(N U L L,FA L S E,N U L L); |
I n i t i a l i z e C r i t i c a l S e c t i o n ( & e s ); |
清除 |
C l o s e H a n d l e(h m t x); |
D e l e t e C r i t i c a l S e c t i o n(& c s); |
無限等待 |
Wa i t F o r S i n g l e O b j e c t(h m t x , I N F I N I T E); |
E n t e r C r i t i c a l S e c t i o n(& c s); |
0等待 |
Wa i t F o r S i n g l e O b j e c t Tr y(h m t x , 0); |
E n t e r C r i t i c a l S e c t i o n(& c s); |
任意等待 |
Wa i t F o r S i n g l e O b j e c t(h m t x , d w M i l l i s e c o n d s); |
不能 |
釋放 |
R e l e a s e M u t e x(h m t x); |
L e a v e C r i t i c a l S e c t i o n(& c s); |
是否能夠等待其他核心對象 |
是(使用Wa i t F o r M u l t i p l e O b j e c t s或類似的函數) |
否 |
表9-2 核心對象與線程同步之間的相互關係
對象 |
何時處於未通知狀態 |
何時處於已通知狀態 |
成功等待的副作用 |
進程 |
當進程仍然活動時 |
當進程終止運行時(E x i t P r o c e s s,Te r m i n a t e P r o c e s s) |
無 |
線程 |
當線程仍然活動時 |
當線程終止運行時(E x i t T h r e a d,Te r m i n a t e T h r e a d) |
無 |
作業 |
當作業的時間尚未結束時 |
當作業的時間已經結束時 |
無 |
檔案 |
當I / O請求正在處理時 |
當I / O請求處理完畢時 |
無 |
控制台輸入 |
不存在任何輸入 |
當存在輸入時 |
無 |
檔案修改通知 |
沒有任何檔案被修改 |
當檔案系統發現修改時 |
重設通知 |
自動重設事件 |
R e s e t E v e n t , P u l s e - E v e n t或等待成功 |
當調用S e t E v e n t / P u l s e E v e n t時 |
重設事件 |
人工重設事件 |
R e s e t E v e n t或P u l s e E v e n t |
當調用S e t E v e n t / P u l s e E v e n t時 |
無 |
自動重設等待定時器 |
C a n c e l Wa i t a b l e Ti m e r或等待成功 |
當時間到時(S e t Wa i t a b l e Ti m e r) |
重設定時器 |
人工重設等待定時器 |
C a n c e l Wa i t a b l e Ti m e r |
當時間到時(S e t Wa i t a b l e Ti m e r) |
無 |
信標 |
等待成功 |
當數量> 0時(R e l e a s e S e m a p h o r e) |
數量遞減1 |
互斥對象 |
等待成功 |
當未被線程擁有時(R e l e a s e互斥對象) |
將所有權賦予線程 |
關鍵程式碼片段(使用者方式) |
等待成功((Tr y)E n t e r C r i t i c a l S e c t i o n) |
當未被線程擁有時(L e a v e C r i t i c a l S e c t i o n) |
將所有權賦予線程 |