|
- 多執行緒之執行緒同步Mutex (功能與CriticalSection相同,保證某一時刻只有一個執行緒能夠訪問共享資源,但是是內核對象,所以訪問速度要比CriticalSection要慢,但是增加了等待超時的功能,使用時可以根據實際的情況選擇其一)
- 一 Mutex
- 互斥對像(mutex)內核對像能夠確保執行緒擁有對單個資源的互斥訪問權。實際上互斥對象是因此而得名的。互斥對像包含一個使用數量,一個執行緒ID和一個遞歸計數器。
- 互斥對象的行為特性與關鍵代碼段相同,但是互斥對像屬於內核對象,而關鍵代碼段則屬於用戶方式對象。這意味著互斥對象的運行速度比關鍵代碼段要慢。但是這也意味著不同進程中的多個執行緒能夠訪問單個互斥對象,並且這意味著執行緒在等待訪問資源時可以設定一個超時值。
- ID用於標識系統中的哪個執行緒當前擁有互斥對象,遞歸計數器用於指明該執行緒擁有互斥對象的次數。
- 互斥對像有許多用途,屬於最常用的內核對像之一。通常來說,它們用於保護由多個執行緒訪問的記憶體塊。如果多個執行緒要同時訪問記憶體塊,記憶體塊中的資料就可能遭到破壞。互斥對像能夠保證訪問記憶體塊的任何執行緒擁有對該記憶體塊的獨佔訪問權,這樣就能夠保證資料的完整性。
- 互斥對象的使用規則如下:
- . 如果執行緒ID是0(這是個無效ID),互斥對像不被任何執行緒所擁有,並且發出該互斥對象的通知信號。
- . 如果ID是個非0數字,那麼一個執行緒就擁有互斥對象,並且不發出該互斥對象的通知信號。像
- . 與所有其他內核對像不同, 互斥對像在操作系統中擁有特殊的代碼,允許它們違反正常的規則。
- 若要使用互斥對象,必須有一個進程首先調用CreateMutex,以便創建互斥對像:
- HANDLECreateMutex(
- PSECURITY_ATTRIBUTES psa,
- BOOL fInitialOwner,
- PCTSTR pszName);
- InitialOwner參數用於控制互斥對象的初始狀態。如果傳遞FALSE(這是通常情況下傳遞的值),那麼互斥對象的ID和遞歸計數器均被設置為0。這意味著該互斥對像沒有被任何執行緒所擁有,因此要發出它的通知信號。
- 如果為fInitialOwner參數傳遞TRUE,那麼該對象的執行緒ID被設置為調用執行緒的ID,遞歸計數器被設置為1。由於ID是個非0數字,因此該互斥對像開始時不發出通知信號。
- 通過調用一個等待函數,並傳遞負責保護資源的互斥對象的句柄,執行緒就能夠獲得對共享資源的訪問權。在內部,等待函數要檢查執行緒的ID,以瞭解它是否是0(互斥對像發出通知信號)。如果執行緒ID是0,那麼該執行緒ID被設置為調用執行緒的ID,遞歸計數器被設置為1,同時,調用執行緒保持可調度狀態。
- 如果等待函數發現ID不是0(不發出互斥對象的通知信號),那麼調用執行緒便進入等待狀態。系統將記住這個情況,並且在互斥對象的ID重新設置為0時,將執行緒ID設置為等待執行緒的ID,將遞歸計數器設置為1,並且允許等待執行緒再次成為可調度執行緒。與所有情況一樣,對互斥內核對像進行的檢查和修改都是以原子操作方式進行的。
- 一旦執行緒成功地等待到一個互斥對象,該執行緒就知道它已經擁有對受保護資源的獨佔訪問權。試圖訪問該資源的任何其他執行緒(通過等待相同的互斥對像)均被置於等待狀態中。當目前擁有對資源的訪問權的執行緒不再需要它的訪問權時,它必須調用ReleaseMutex函數來釋放該互斥對像:
- BOOL ReleaseMutex(HANDLE hMutex);
- 該函數將對象的遞歸計數器遞減1。
- 當該對像變為已通知狀態時,系統要查看是否有任何執行緒正在等待互斥對象。如果有,系統將「按公平原則」選定等待執行緒中的一個,為它賦予互斥對象的所有權。當然,這意味著執行緒ID被設置為選定的執行緒的ID,並且遞歸計數器被置為1。如果沒有其他執行緒正在等待互斥對象,那麼該互斥對像將保持已通知狀態,這樣,等待互斥對象的下一個執行緒就立即可以得到互斥對象。
- 二 API
- [tr] Mutex function Description [/tr] CreateMutex Creates or opens a named or unnamed mutex object.
- CreateMutexEx Creates or opens a named or unnamed mutex object and returns a handle to the object.
- OpenMutex Opens an existing named mutex object.
- ReleaseMutex Releases ownership of the specified mutex object.
- 三 實例
- 來自msdn的實例:線上程函數中有一個循環,在每個循環的開始都取得Mutex,然後對全局或靜態操作,相當於在關鍵代碼段操作,然後在使用完以後釋放它,大家可以執行,查看結果。
- #include <windows.h>
- #include <stdio.h>
- #define THREADCOUNT 64 //less than 64
- HANDLE ghMutex;
- int g_x =
- 0;
- DWORD WINAPI WriteToDatabase(LPVOID);
- void main()
- {
- HANDLE aThread[THREADCOUNT];
- DWORD ThreadID;
- int i;
- // Create a mutex with no initial owner
- ghMutex = CreateMutex(
- NULL, // default security attributes
- FALSE, // initially not owned
- NULL); // unnamed mutex
- if (ghMutex == NULL)
- {
- printf("CreateMutex error: %d\n"、GetLastError());
- return;
- }
- // Create worker threads
- for( i=0; i < THREADCOUNT; i++ )
- {
- aThread = CreateThread(
- NULL, // default security attributes
- 0, // default stack size
- (LPTHREAD_START_ROUTINE) WriteToDatabase、
- NULL, // no thread function arguments
- 0, // default creation flags
- &ThreadID); // receive thread identifier
- if( aThread == NULL )
- {
- printf("CreateThread error: %d\n"、GetLastError());
- return;
- }
- }
- // Wait for all threads to terminate
- WaitForMultipleObjects(THREADCOUNT、aThread、TRUE、INFINITE);
- // Close thread and mutex handles
- for( i=0; i < THREADCOUNT; i++ )
- CloseHandle(aThread);
- CloseHandle(ghMutex);
- printf("g_x is :%d\n",g_x);
- }
- DWORD WINAPI WriteToDatabase( LPVOID lpParam )
- {
- DWORD dwCount=0、dwWaitResult;
- // Request ownership of mutex.
- while( dwCount <
- 100 )
- {
- dwWaitResult = WaitForSingleObject(
- ghMutex, // handle to mutex
- INFINITE); // no time-out interval
-
- switch (dwWaitResult)
- {
- // The thread got ownership of the mutex
- case WAIT_OBJECT_0:
- __try {
- g_x++;
- // TODO: Write to the database
- printf("Thread %d writing to database\n"、
- GetCurrentThreadId());
- dwCount++;
- }
- __finally {
- // Release ownership of the mutex object
- if (! ReleaseMutex(ghMutex))
- {
- // Deal with error.
- }
- }
- break;
- // The thread got ownership of an abandoned mutex
- case WAIT_ABANDONED:
- return FALSE;
- }
- }
- return TRUE;
- }
- 四 參考
複製代碼 |
|