接上一篇,我们来看另一种实现临界区顺序访问的方法
这次我们使用,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; }