0

我有这两种方法可以对对象进行线程独占访问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对象(或类似的东西)来做,当对象超出范围时解锁缓冲区。

那怎么可能呢?

4

3 回答 3

2

一种方法是使用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
}
于 2009-08-10T15:26:37.010 回答
1

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

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 对象的访问,所以它不应该给用户任何在锁被释放后挂起和变异的东西。

于 2009-08-10T15:21:13.307 回答
1

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

#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();
}
于 2009-08-10T16:14:32.687 回答