如何使用 CSingleLock 提供对该缓冲区的访问?

Posted

技术标签:

【中文标题】如何使用 CSingleLock 提供对该缓冲区的访问?【英文标题】:How can I provide access to this buffer with CSingleLock? 【发布时间】:2009-08-10 15:06:45 【问题描述】:

我有这两种方法可以线程独占访问CMyBuffer 对象:

标题:

class CSomeClass

//...
public:
    CMyBuffer & LockBuffer();
    void ReleaseBuffer();

private:
    CMyBuffer m_buffer;
    CCriticalSection m_bufferLock;
//...

实施:

CMyBuffer & CSomeClass::LockBuffer()

    m_bufferLock.Lock();
    return m_buffer;


void CSomeClass::ReleaseBuffer()

    m_bufferLock.Unlock();

用法:

void someFunction(CSomeClass & sc)

    CMyBuffer & buffer = sc.LockBuffer();
    // access buffer
    sc.ReleaseBuffer();

我喜欢的是, 用户只需执行一项功能 调用并且只能访问缓冲区 锁定后。 我不知道的 就像是用户必须释放 明确的。

更新:Nick Meyer 和 Martin York 指出了这些额外的缺点:

用户可以释放锁,然后使用缓冲区。 如果在释放锁之前发生异常,缓冲区保持锁定状态。

我想用CSingleLock 对象(或类似的东西)来做,当对象超出范围时解锁缓冲区。

这怎么可能?

【问题讨论】:

【参考方案1】:

一种方法是使用RAII:

class CMyBuffer

public:
    void Lock()
    
        m_bufferLock.Lock();
    

    void Unlock()
    
        m_bufferLock.Unlock();
    

private:
    CCriticalSection m_bufferLock;

;

class LockedBuffer

public:
    LockedBuffer(CMyBuffer& myBuffer) : m_myBuffer(myBuffer)
    
        m_myBuffer.Lock();
    

    ~LockedBuffer()
    

        m_myBuffer.Unlock();
    

    CMyBuffer& getBuffer() 
    
        return m_myBuffer;
    

private:
    CMyBuffer& m_myBuffer;

;

class CSomeClass

    //...
public:
    LockedBuffer getBuffer();

private:
    CMyBuffer m_buffer;

;


LockedBuffer CSomeClass::getBuffer()

    return LockedBuffer(m_buffer);


void someFunction(CSomeClass & sc)

    LockedBuffer lb = sc.getBuffer();
    CMyBuffer& buffer = lb.getBuffer();
    //Use the buffer, when lb object goes out of scope buffer lock is released

【讨论】:

在这种情况下,用户可以继续使用返回的 CMyBuffer & 并在 LockedBuffer 对象被销毁后继续使用它。 @Nick:原始代码也可以。至少这个版本可以防止异常问题。【参考方案2】:

使用表示缓冲区的对象。 当这个对象被初始化时获取锁,当它被销毁时释放锁。 添加一个强制转换运算符,以便在任何函数调用中都可以使用它来代替缓冲区:

#include <iostream>

// Added to just get it to compile
struct CMyBuffer
    void doStuff() std::cout << "Stuff\n";;
struct CCriticalSection

        void Lock()     
        void Unlock()   
;          

class CSomeClass

    private:
        CMyBuffer m_buffer;
        CCriticalSection m_bufferLock;

        // Note the friendship.
        friend class CSomeClassBufRef;
;

// The interesting class.
class CSomeClassBufRef

    public:
        CSomeClassBufRef(CSomeClass& parent)
            :m_owned(parent)
        
           // Lock on construction
            m_owned.m_bufferLock.Lock();
        
        ~CSomeClassBufRef()
        
            // Unlock on destruction
            m_owned.m_bufferLock.Unlock();
        
        operator CMyBuffer&()
        
            // When this object needs to be used as a CMyBuffer cast it.
            return m_owned.m_buffer;
        
    private:
        CSomeClass&     m_owned;
; 

void doStuff(CMyBuffer& buf)
           
    buf.doStuff();

int main()

    CSomeClass          s;

    // Get a reference to the buffer and auto lock.
    CSomeClassBufRef    b(s);

    // This call auto casts into CMyBuffer
    doStuff(b);

    // But you can explicitly cast into CMyBuffer if you need.
    static_cast<CMyBuffer&>(b).doStuff();

【讨论】:

你怎么看重载operator->()?这样就可以说 b->doStuff() 而不必进行 static_cast 附录:我认为 static_cast 被制作得如此丑陋,以使其引人注目且不那么琐碎。但在这种情况下,它实际上应该这样做,所以没有必要让这个演员比用户需要的更难。 我目前正在尝试并注意到, operator*() 也应该重载。 我个人不会定义 operator*() 或 operator->()。我会使用强制转换运算符或可能的 get() 方法(在 CSomeClassBufRef 上)【参考方案3】:

恕我直言,如果您的目标是阻止用户仅在缓冲区被锁定时访问它,那么您正在打一场棘手的战斗。考虑用户是否这样做:

void someFunction(CSomeClass & sc)

   CMyBuffer & buffer = sc.LockBuffer();
   sc.ReleaseBuffer();
   buffer.someMutatingMethod(); // Whoops, accessed while unlocked!

为了允许用户访问缓冲区,您必须返回对缓冲区的引用,他们总是会犯错误,一直持有到锁定释放后。

也就是说,您也许可以这样做:

class CMyBuffer

   private:
      void mutateMe();
      CCriticalSection m_CritSec;

   friend class CMySynchronizedBuffer;
;

class CMySynchronizedBuffer

   private:
      CMyBuffer & m_Buffer;
      CSingleLock m_Lock

   public:
      CMySynchronizedBuffer (CMyBuffer & buffer)
         : m_Buffer (buffer)
         , m_Lock (&m_Buffer.m_CritSec, TRUE)
      
      

      void mutateMe()
      
         m_Buffer.mutateMe();
      
;

像这样使用:


   CMyBuffer buffer;  // Or declared elsewhere
   // buffer.mutateMe();  (Can't do this)
   CMySyncrhonizedBuffer synchBuffer (buffer); // Wrap it & lock it
   synchBuffer.mutateMe();  // Now protected by critical section
 // synchBuffer and its CSingleLock member are destroyed and the CS released

这迫使用户将 CMyBuffer 对象包装在 CMySynchronizedBuffer 对象中,以便获取其任何变异方法。因为它实际上并没有通过返回一个引用来提供对底层 CMyBuffer 对象的访问,所以它不应该给用户任何可以在锁被释放后挂起和变异的东西。

【讨论】:

以上是关于如何使用 CSingleLock 提供对该缓冲区的访问?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Web Audio API 移动/调制音频缓冲频率

如果标准 SQL 不提供 _PARTITIONTIME 字段,我如何在 BigQuery 中查询流缓冲区

如何捕获麦克风缓冲区

如何在Blackberry中缓冲音频?

IOS操作完成后如何释放vImage缓冲区

如何设置 MediaCodec 创建的编码器缓冲区大小