Как я могу предоставить доступ к этому буферу с CSingleLock? - PullRequest
0 голосов
/ 10 августа 2009

У меня есть два метода эксклюзивного доступа к объекту 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();
}
  • Что мне нравится в этом, так это то, что пользователь должен сделать только один вызов функции и может получить доступ к буферу только после его блокировки.
  • Что я не делаюМне кажется, что пользователь должен выпустить явно.

Обновление : Ник Мейер и Мартин Йорк указали на эти дополнительные недостатки:

  • Пользователь может снять блокировку и затем использовать буфер.
  • Если перед снятием блокировки возникает исключение, буфер остается заблокированным.

Я хотел бы сделать этос CSingleLock объектом (или чем-то подобным), который разблокирует буфер, когда объект выходит из области видимости.

Как это можно сделать?

Ответы [ 3 ]

2 голосов
/ 10 августа 2009

Один из способов сделать это - использовать 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
}
1 голос
/ 10 августа 2009

Использовать объект, представляющий буфер.
Когда этот объект инициализирован, получите блокировку, а когда он будет уничтожен, снимите блокировку.
Добавьте оператор приведения, чтобы его можно было использовать вместо буфера при любом вызове функции:

#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();
}
1 голос
/ 10 августа 2009

ИМХО, если ваша цель состоит в том, чтобы запретить пользователю доступ к буферу только тогда, когда он заблокирован, вы вступаете в сложную битву. Подумайте, делает ли пользователь:

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 путем возврата ссылки, он не должен давать пользователю ничего, за что можно зависнуть и изменить его после снятия блокировки.

...