接上一篇,我們來看另一種實現臨界區順序訪問的方法
這次我們使用,InitializeCriticalSection, EnterCriticalSection,LeaveCriticalSection,DeleteCriticalSection
要使用這些需要包含windows.h並定義一個CRITICAL_SECTION變數
那麼這種方式與上一篇中用互斥量實現的方式有什麼差別呢?
下面是實現的代碼:
View Code
#pragma once#include <windows.h>#include <assert.h>class CVCriSection{public: CVCriSection(void); ~CVCriSection(void);public: int CsEnter() { EnterCriticalSection(&m_cs); return 0; } int CsLeave() { LeaveCriticalSection(&m_cs); return 0; }protected: CRITICAL_SECTION m_cs;};class CVCriSectionAuto{public: CVCriSectionAuto(CVCriSection* cs) { m_cs=cs; m_cs->CsEnter(); } ~CVCriSectionAuto(void) { m_cs->CsLeave(); m_cs=NULL; }private: CVCriSection* m_cs;};CVCriSection::CVCriSection(void){ InitializeCriticalSection(&m_cs);}CVCriSection::~CVCriSection(void){ DeleteCriticalSection(&m_cs);}
有如下的測試代碼
CVCriSection cs;int sum=0;int last=0;pthread_mutex_t g_mutex = CreateMutex(NULL, FALSE, NULL);DWORD WINAPI worker(LPVOID lpParameter){ int no= (int)lpParameter; last = no; while (1) { CVCriSectionAuto csauto(&cs); //方式1 //CCriticalSection cs(g_mutex); //方式2,這種方式下會嚴格按照0, 1, 2, 0, 1, 2 .... 或 1, 0, 2, 1, 0, 2 等, 既3個線程具有均等的運行機會 //cs.CsEnter(); sum=0; for (int i=0; i<=100000; i++) sum += i; if (no != (last+1)%3) printf("not equal\n"); last = no; //cs.CsLeave(); }}int main(){ for (int i=0; i<3; i++) CreateThread(NULL, 0, worker, (LPVOID)i, 0, NULL); Sleep(20000); return 0;}
當使用方式1(使用CRITICAL_SECTION實現)來實現互斥訪問時,上面的代碼將會有很多not equal輸出。這就是說,三個線程執行的次數是隨機的
當使用方式2(使用互斥量實現)來實現互斥訪問時,上面的代碼將不會有not equal輸出。也就是說,三個線程得到了均等的運行機會。它們訪問臨界區的次數是相當的。
下面的測試代碼可以更直觀的看出它們的差別:
CVCriSection cs;int sum=0;int last=0;int cnt[3] = {0, 0, 0};pthread_mutex_t g_mutex = CreateMutex(NULL, FALSE, NULL);DWORD WINAPI worker(LPVOID lpParameter){ int no= (int)lpParameter; last = no; while (1) { //CVCriSectionAuto csauto(&cs); //方式1 CCriticalSection cs(g_mutex); //方式2,這種方式下會嚴格按照0, 1, 2, 0, 1, 2 .... 或 1, 0, 2, 1, 0, 2 等, 既3個線程具有均等的運行機會 //cs.CsEnter(); sum=0; for (int i=0; i<=100000; i++) sum += i; cnt[no]++; //cs.CsLeave(); }}int main(){ for (int i=0; i<3; i++) CreateThread(NULL, 0, worker, (LPVOID)i, 0, NULL); Sleep(5000); for (int j=0; j<3; j++) printf("%d\n", cnt[j]); return 0;}
最後分別輸出三個線程分別進入臨界區的次數。
總結:
用CRITICAL_SECTION實現的方式,線程訪問臨界區的次數基本上是和線程本身執行的快慢成正比的
用互斥量實現的方式,線程訪問臨界區的次數基本上是相等的。
所以要根據實際使用的情境選擇合適的方式
進一步可有用下面的代碼來驗證上面的結論
View Code
CVCriSection cs;int sum=0;int last=0;int cnt[3] = {0, 0, 0};pthread_mutex_t g_mutex = CreateMutex(NULL, FALSE, NULL);DWORD WINAPI worker(LPVOID lpParameter){ int no= (int)lpParameter; last = no; while (1) { CVCriSectionAuto csauto(&cs); //方式1 //CCriticalSection cs(g_mutex); //方式2,這種方式下會嚴格按照0, 1, 2, 0, 1, 2 .... 或 1, 0, 2, 1, 0, 2 等, 既3個線程具有均等的運行機會 //cs.CsEnter(); sum=0; int add = no*1000; for (int i=0; i<=10000+add; i++) sum += i; cnt[no]++; //cs.CsLeave(); }}int main(){ for (int i=0; i<3; i++) CreateThread(NULL, 0, worker, (LPVOID)i, 0, NULL); Sleep(5000); for (int j=0; j<3; j++) printf("%d %d\n", j, cnt[j]); return 0;}