windows 临界区

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

 

 

 

你可能感兴趣的:(windows 临界区)