1. 程式人生 > >C++多執行緒之使用Mutex和Critical_Section

C++多執行緒之使用Mutex和Critical_Section

Mutex和Critical Section都是主要用於限制多執行緒(Multithread)對全域性或共享的變數、物件或記憶體空間的訪問。下面是其主要的異同點(不同的地方用綠色表示)。

 

Mutex

Critical Section

效能和速度

慢。

Mutex 是核心物件,相關函式的執行 (WaitForSingleObject,

ReleaseMutex)需要使用者模式(User Mode)到核心模式

(Kernel Mode)的轉換,在x86處理器上這種轉化一般要

發費600個左右的 CPU指令週期。

快。

Critical Section本身不是核心物件,相關函式

(EnterCriticalSection,LeaveCriticalSection)

的呼叫一般都在使用者模式內執行,在x86處理器上

一般只需要發費9個左右的 CPU指令週期。只有

當想要獲得的鎖正好被別的執行緒擁有時才會退化

成和Mutex一樣,即轉換到核心模式,發費600個

左右的 CPU指令週期。

能否跨越程序(Process)邊界

可以

不可

定義寫法

HANDLE hmtx;

CRITICAL_SECTION cs;

初始化寫法

hmtx= CreateMutex (NULL, FALSE, NULL);

InitializeCriticalSection(&cs);

結束清除寫法

CloseHandle(hmtx);

DeleteCriticalSection(&cs);

無限期等待的寫法

WaitForSingleObject (hmtx, INFINITE);

EnterCriticalSection(&cs);

0等待(狀態檢測)的寫法

WaitForSingleObject (hmtx, 0);

TryEnterCriticalSection(&cs);

任意時間等待的寫法

WaitForSingleObject (hmtx, dwMilliseconds);

不支援

鎖釋放的寫法

ReleaseMutex(hmtx);

LeaveCriticalSection(&cs);

能否被一道用於等待其他核心物件

可以(使用WaitForMultipleObjects,

WaitForMultipleObjectsEx,

MsgWaitForMultipleObjects,

MsgWaitForMultipleObjectsEx等等)

不可

當擁有鎖的執行緒死亡時

Mutex變成abandoned狀態,其他的等待執行緒可以獲得鎖。

Critical Section的狀態不可知(undefined),

以後的動作就不能保證了。

自己會不會鎖住自己

不會(對已獲得的Mutex,重複呼叫WaitForSingleObject不會

鎖住自己。但最後你別忘了要呼叫同樣次數的

ReleaseMutex)

不會(對已獲得的Critical Section,重複呼叫

EnterCriticalSection不會鎖住自己。但最後

你別忘了要呼叫同樣次數的

LeaveCriticalSection)

 

下面是一些補充:

l         請先檢查你的設計,把不必要的全域性或共享物件改為區域性物件。全域性的東西越少,出問題的可能就越小。

l         每次你使用EnterCriticalSection時,請不要忘了在函式的所有可能返回的地方都加上LeaveCriticalSection。對於Mutex也同樣。若你把這個問題和Win32 structured exception或C++ exception一起考慮,你會發現問題並不是那麼簡單。自定義一個封裝類可能是一種解決方案,以Critical Section為例的程式碼如下所示:

class csholder

{

    CRITICAL_SECTION *cs;

public:

    csholder(CRITICAL_SECTION *c): cs(c)

    { EnterCriticalSection(cs); }

    ~csholder() { LeaveCriticalSection(cs); }

};

 

CRITICAL_SECTION some_cs;

void foo()

{

    // ...

    csholder hold_some(&some_cs);

 

    // ... CS protected code here

 

    // at return or if an exception happens

    // hold_some's destructor is automatically called

}

l         根據你的互斥範圍需求的不同,把Mutex或Critical Section定義為類的成員變數,或者靜態類變數。

l         若你想限制訪問的全域性變數只有一個而且型別比較簡單(比如是LONG或PVOID型),你也可以使用InterlockedXXX系列函式來保證一個執行緒寫多個執行緒讀。

Demo程式

#include <Windows.h>
#include <iostream>
using namespace std;
int index = 0;
// 臨界區結構物件
CRITICAL_SECTION g_cs;
HANDLE hMutex = NULL;
void changeMe()
{
 cout << index++ << endl;
}
void changeMe2()
{
 cout << index++ << endl;
}
void changeMe3()
{
 cout << index++ << endl;
}
DWORD WINAPI th1(LPVOID lpParameter)
{
 while(1)
 {
  Sleep(1600); //sleep 1.6 s
  // 進入臨界區
  EnterCriticalSection(&g_cs);
  // 等待互斥物件通知
  //WaitForSingleObject(hMutex, INFINITE);
  // 對共享資源進行寫入操作
  //cout << "a" << index++ << endl;
  changeMe();
  changeMe2();
  changeMe3();
  // 釋放互斥物件
  //ReleaseMutex(hMutex);
  // 離開臨界區
  LeaveCriticalSection(&g_cs);  
 }
 return 0;
}
DWORD WINAPI th2(LPVOID lpParameter)
{
 while(1)
 {
  
  Sleep(2000); //sleep 2 s
  // 進入臨界區
  EnterCriticalSection(&g_cs);

  // 等待互斥物件通知
  //WaitForSingleObject(hMutex, INFINITE);
  //cout << "b" << index++ << endl;
  changeMe();
  changeMe2();
  changeMe3();
  // 釋放互斥物件
  //ReleaseMutex(hMutex);

  // 離開臨界區
  LeaveCriticalSection(&g_cs); 
 }
 return 0;
}
int main(int argc, char* argv[])
{
 // 建立互斥物件
 //hMutex = CreateMutex(NULL, TRUE, NULL);
 // 初始化臨界區
 InitializeCriticalSection(&g_cs);
 HANDLE hThread1;
 HANDLE hThread2;
 hThread1 = CreateThread(NULL, 0, th1,  NULL, 0, NULL);
 hThread2 = CreateThread(NULL, 0, th2,  NULL, 0, NULL);
 int k; 
 cin >> k; 
 printf("Hello World!\n");
 return 0;
}
--------------------- 
作者:LoveApp_Han 
來源:CSDN 
原文:https://blog.csdn.net/xdrt81y/article/details/17005235 
版權宣告:本文為博主原創文章,轉載請