用內核對象同步線程

 

       用戶方式同步具有速度快的優點。但是它的缺點特是顯而易見的。
  • 首先,用互鎖函數家族的時候,這些函數只能用在單值數據上
  • 如果用關鍵代碼段的話,只能對單個進程中的線程進行同步。而且容易出現死瑣,而且不能設置等待時間
那麼內核對象就克服了用戶方式同步的幾個缺點,能夠在不同進程間對線程實施同步,而且處理完全不止在單值數據上。但是內核方式同步線程時,必須將線程進入到內核方式,這樣就使處理速度大大降低。這也是內核方式的缺點,所以還是能用用戶方式處理的就用用戶方式來處理。
現在來說下一個很多內核對象都具有的特性,這就是signaled/nonsignaled特性。
這個特性是這樣的:在內核對象處於運行狀態時,他就是nonsignaled狀態,當內核對象運行結束時,就會處於signaled狀態。在內核對象中是用一個bool值來表示的。所以查看內核對象的狀態很容易。只要查這個值就可以了。所以當線程要等待的內核對象處於signaled狀態時,那麼這個線程就可以被CPU調度,否則處於等待狀態。
擁有這個特性的內核對象有:

· Processes
· File change notifications
· Threads
· Events
· Jobs
· Waitable timers
· Files
· Semaphores
· Console input
· Mutexes
現在就來重點說下Event,waitable timer,Semaphore,Mutex.這四個用的比較多。
在說這四個內核對象之前先來說下能讓線程進入等待狀態的函數,也就是去等待上面四個內核對象的狀態的函數。他們有
DWORD WaitForSingleObject(HANDLE hObject,DWORD dwMillisecounds)
參數:
  • 1個參數就是要等待的對象的HANDLE,可以是上面四個中的一個。
  • 2個參數是等待超時時間。
返回值
  • WAIT_OBJECT_0//成功等待。也就是內核對象已經爲signaled,線程成爲可調度線程
  • WAIT_TIMEOUT//因爲超時,線程重新喚醒,成爲可調度的
  • WAIT_FAILED//將一個錯誤的值傳遞給WaitForSingleObject,比如傳遞了一個無效句柄。
DWORD WaitForMultipleObjects(DWORD dwCount,CONST HANDLE *phObjects,BOOL fWaitAll,DWORD dwMillisecounds)
參數:
  • dwCount  //想要讓函數查看的內核對象的數目,這個值必須在1MAXIMUN_WAIT_OBJECTS(64)
  • phObjects    //指向內核對象句柄的數組的指針
  • fWaitAll      //如果爲TRUE,就是等待到所有內核對象都signaledFALSE則是只要有一個signaled就喚醒
  • dwMillisecounds     //超時時間
返回值:
WaitForSingleObject一樣。只是,如果fWaitAll==false。那麼如果想得到哪個對象signaled就可以將返回值剪去WAIT_OBJECT_0。(前提是,返回值即不是WAIT_TIMEOUT也不是WAIT_FAILED
其實再調用上面兩個函數時,如果等待成功(就是返回值爲WAIT_OBJECT_0),會對內核對象產生一個副作用。有的內核對象的副作用就是改變的內核對象的狀態(比如自動清楚內核對象)。有的內核對象則沒有任何副作用(比如線程,進程內核對象)。
下面就來逐一介紹Event,Waitable timer,Semaphrone,Mutex對象
先是Event對象。Event對象是最基本的內核對象。它包括:
1.        使用計數器
2.       自動重置/人工重置開關
3.       signaled/nonsignal開關
13不用多介紹。 重點說下2。什麼叫自動重置?就是所有等待的線程,當Event對象變成signaled狀態時,只會隨即的挑選一個線程進入可調度狀態。然後Event對象會被自動置爲nonsignaled狀態。而人工重置是當Event對象變成signaled時候,所有等待的線程都會被置爲可調度狀態。CPU會給他們都分配時間片。而且不會自動將Event狀態置爲nonsignaled。要通過ResetEventEvent變爲nonsignaled
至於創建Event內核對象CreateEvent函數,可以查找MSDN
然後就是WaitableTimer內核對象。
CreateWaitableTimer OpenWaitableTimer的使用可以查找MSDN。重點說下設置WaitableTimer
設置WaitableTimer使用SetWaitbaleTimer。原形如下:
BOOL SetWaitableTimer(
    HANDLE hTimer, // handle to a timer object
    const LARGE_INTEGER *pDueTime, // when timer will become signaled
    LONG lPeriod, // periodic timer interval
    PTIMERAPCROUTINE pfnCompletionRoutine, // pointer to the completion routine
    LPVOID lpArgToCompletionRoutine, // data passed to the completion routine
    BOOL fResume // flag for resume state
   );
參數:
  • *pDueTime是等待定時器第1次報時的時間。可以爲這個參數傳遞0,表示調用函數的時候就報時。或者傳遞一個負值,表示調用這個函數開始延遲n*100ns後開始第1次報時。
  • pfnCompletionRoutine指向一個函數的地址。如果這項不爲NULL,那麼當定時器報時的時候,會講這個函數放入線程的APC隊列。(什麼是APC會在以後詳細介紹)
下面再來說下 Semaphore
Semaphroe的作用是爲一個資源計數,而這個資源又是多個的。比如說你想對一個擁有10個元素的數組進行同步。那麼就可以用Semaphore。當10個元素都被佔用的時候。Semaphore就處於nonsignaled狀態,反之,有元素可以被處理的時候就處於nignaled狀態。
Semapore包括:
1.       使用計數器
2.       最大資源數量
3.       當前可使用資源數量
他的使用規則是:
1.       當前資源大於0,發出信標信號(處於signaled狀態)
2.       當前資源等於0,不發出信號
3.       0<=當前資源<=最大資源
CreateSemaphore OpenSemaphore ReleaseSemaphore可以查看MSDN
ReleaseSemaphore是將Semaphore的可使用資源進行增加。
最後就是Mutex了。Mutex是使用的最多的線程同步內核對象。它的作用是保證線程對單個資源的互斥訪問權。它包括:
  • 使用計數器
  • 擁有Mutex的線程ID
  • 遞歸計數器//該線程擁有互斥對象的次數
Mutex的使用規則是:
  • 線程ID0,互斥對象不被任何線程所擁有,並且發出該互斥對象的通知信號
  • 線程ID不爲0,那麼線程就擁有該互斥對象,不發出通知信號
CreateMutx OpenMutex ReleaseMutex查看MSDN
下面是互斥對象與關鍵代碼段的比較

Characteristic
Mutex
Critical Section
Performance
Slow
Fast
Can be used across process boundaries
Yes
No
Declaration
HANDLE hmtx;
CRITICAL_SECTION cs;
Initialization
hmtx= CreateMutex (NULL, FALSE, NULL);
InitializeCriticalSection(&cs);
Cleanup
CloseHandle(hmtx);
DeleteCriticalSection(&cs);
Infinite wait
WaitForSingleObject (hmtx, INFINITE);
EnterCriticalSection(&cs);
0 wait
WaitForSingleObject (hmtx, 0);
TryEnterCriticalSection(&cs);
Arbitrary wait
WaitForSingleObject (hmtx, dwMilliseconds);
Not possible
Release
ReleaseMutex(hmtx);
LeaveCriticalSection(&cs);
Can be waited on with other kernel objects
Yes (use WaitForMultipleObjects or similar function)
No
 
 
發佈了48 篇原創文章 · 獲贊 2 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章