关于CRITICAL_SECTION用法的问题

时间:2011-03-24 15:14:06

标签: c++ winapi visual-c++

鉴于以下代码,

class MyCriticalSection : protected CRITICAL_SECTION
{
public:
    MyCriticalSection()
    {
        InitializeCriticalSection(this);
        EnterCriticalSection(this);
    }
    ~MyCriticalSection()
    {
        LeaveCriticalSection(this);
        DeleteCriticalSection(this);
    }
};

class MyClass
{
public:
    MyClass() : m_Int(0) {}

    int GetNum() 
    { 
        MyCriticalSection myCriticalSectionA;  // LockA

        return m_Int; 
    }

    int SetNum(int i)
    {
        MyCriticalSection myCriticalSectionB;  // LockB

        m_Int = i;
    }
protected:
    int m_Int;
};

我认为上述设计是正确的,如果您发现一些错误,请纠正我。 LockA的使用有两个功能:1>它阻止了多个线程同时调用GetNum函数。 2 - ;它阻止函数GetNum和SetNum同时访问m_Int。

这是我的问题: 变量myCriticalSectionA和myCriticalSectionB是MyCriticalSection类的两个不同实例。为什么他们可以相互协作,以便锁定机制按预期工作?

我的猜测是CRITICAL_SECTION不知何故有一些静态标志,以便它的不同实例可以相互通信。

如果我错了,请纠正我。

====更新====

class MyCriticalSection : protected CRITICAL_SECTION
{
public:
    MyCriticalSection()
    {
        InitializeCriticalSection(this);

    }
    ~MyCriticalSection()
    {       
        DeleteCriticalSection(this);
    }
    void Lock()
    {
        EnterCriticalSection(this);
    }
    void UnLock()
    {
        LeaveCriticalSection(this);
    }
};

class MyClass
{
public:
    MyClass() : m_Int(0) {}

    int GetNum() 
    { 
        m_Lock.Lock();
        // do something here
        m_Lock.UnLock();
        return m_Int; 
    }

    int SetNum(int i)
    {
        m_Lock.Lock();      
        m_Int = i;
        m_Lock.UnLock();
    }
protected:
    int m_Int;
    MyCriticalSection m_Lock;
};

谢谢

5 个答案:

答案 0 :(得分:6)

您的更新并不是更好。如果你想以RAII风格做到这一点,那么你需要更努力地工作。关键是你需要将静态分配的关键部分对象与需要保护的所有块的本地锁分开。

以下内容取自Jonathan Dodds,但这是一种经典模式。

class CriticalSection
{
public:
    CriticalSection()
        { ::InitializeCriticalSection(&m_rep); }
    ~CriticalSection()
        { ::DeleteCriticalSection(&m_rep); }

    void Enter()
        { ::EnterCriticalSection(&m_rep); }
    void Leave()
        { ::LeaveCriticalSection(&m_rep); }

private:
    // copy ops are private to prevent copying
    CriticalSection(const CriticalSection&);
    CriticalSection& operator=(const CriticalSection&);

    CRITICAL_SECTION m_rep;
};

虽然你可以通过继承CRITICAL_SECTION来做到这一点,但我觉得封装更合适。

接下来定义了锁:

class CSLock
{
public:
    CSLock(CriticalSection& a_section)
        : m_section(a_section) { m_section.Enter(); }
    ~CSLock()
        { m_section.Leave(); }

private:
    // copy ops are private to prevent copying
    CSLock(const CSLock&);
    CSLock& operator=(const CSLock&);

    CriticalSection& m_section;
};

最后,一个使用示例:

class Example
{
public:
    bool Process( … );

    …

private:
    CriticalSection m_criticalsection;

    …
};

bool Example::Process( … )
{
    CSLock lock(m_critsection);

    // do some stuff
    …
}

关键是在所有线程中共享一个关键部分的单个实例。这就是使关键部分发挥作用的原因。

在对位中,可能存在许多CSLock的实例,这些实例同时存在于相同的关键部分。这允许许多线程调用Process()方法,但是在单个共享临界区采用的CSLock实例的生命周期内对其代码进行序列化。

答案 1 :(得分:1)

你完全错了。两个CRITICAL_SECTION完全不相关 - 程序如何创建多个锁?查看boost::thread库,了解如何设计界面。

答案 2 :(得分:1)

使用您建议的代码,您将阻止并发读取和并发写入,但不会将这两个操作联锁起来。为了达到你想要的效果,你应该使用一个CriticalSession对象。

答案 3 :(得分:1)

每个线程都有自己的堆栈,并且你的锁变量位于堆栈上,这意味着锁不能在线程之间进行保护。

代码snippit;

class CSLock
{
public:
       CSLock( CriticalSection& cs )
       { 
            EnterCriticalSection( cs );
       }
       ~CSLock()
       {
            LeaveCriticalSection( cs );
       }
 };

 class Client
 {
 public:
       Client()
       {}
       void Fun()
       {
           CSLock( m_cs );
       }
 private:
       CriticalSection m_cs;
 };

答案 4 :(得分:0)

嗯,你离基地很远。

首先,即使我没有任何Windows安装来验证这一点,我很确定你不能像这样继承CRITICAL_SECTION

然后,您使用此设置锁定任何地方,只会减慢速度。 GetSet中的每个条目都会创建并初始化 new 关键部分,该部分特定于该线程和该特定呼叫。你需要在调用之间拥有CS 共享 - 至少让他们成为类成员。