关于CRITICAL_SECTION的使用问题

Posted

技术标签:

【中文标题】关于CRITICAL_SECTION的使用问题【英文标题】:Question about the usage of CRITICAL_SECTION 【发布时间】:2011-03-24 15:14:06 【问题描述】:

给定以下代码,

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;
;

谢谢

【问题讨论】:

是什么让您认为这两个实例实际上是“协作”的(并且代码是正确的)? @aix,我想我错误地假设这个关键部分与 Java 提供的同步方法相似。 -- 谢谢 【参考方案1】:

您的更新也好不到哪里去。如果您想以 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 实例的生命周期内被序列化,在单个共享临界区上。

【讨论】:

谢谢。很高兴你现在被整理出来了。当然,我复制了代码! ;-)【参考方案2】:

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

【讨论】:

根据我的设计,LockA只能防止函数GetNum被多个线程同时调用。它是否正确? -- 谢谢 @q0987:不。每次进入函数时都会创建一个新锁,因此每个线程都会创建一个新锁并锁定它,而不是锁定或尝试锁定同一个锁。跨度> 您能再看看我的更新吗?更新是否正确? ——谢谢【参考方案3】:

使用您提出的代码,您正在阻止并发读取和并发写入,但不会将这两个操作互锁。要实现你想要的,你应该使用一个 CriticalSession 对象。

【讨论】:

【参考方案4】:

每个线程都有自己的栈,而你的锁变量位于栈上,也就是说锁不能在线程之间进行保护。

代码片段;

class CSLock

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

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

【讨论】:

【参考方案5】:

嗯,你离基地还很远。

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

那么,您不会使用此设置在任何地方锁定任何东西,只是减慢速度。每个进入GetSet 的条目都会创建并初始化new 临界区,该临界区特定于该线程和该特定调用。您需要在通话之间共享 CS - 至少让他们成为班级成员。

【讨论】:

以上是关于关于CRITICAL_SECTION的使用问题的主要内容,如果未能解决你的问题,请参考以下文章

关于reduce的使用方法

第十九章 函数的高级话题

经典线程同步 关键段CS

多线程面试题系列:经典线程同步 关键段CS

关于换行符CR和LF的区别

关于 服务器提交了协议冲突. Section=ResponseHeader Detail=CR 后面必须是 LF 错误