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

发布于 2024-07-30 14:17:01 字数 959 浏览 4 评论 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 对象(或类似的东西)来完成此操作,当对象超出范围时,它会解锁缓冲区。

怎么可能呢?

I have these two methods for thread-exclusive access to a CMyBuffer object:

Header:

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

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

Implementation:

CMyBuffer & CSomeClass::LockBuffer()
{
    m_bufferLock.Lock();
    return m_buffer;
}

void CSomeClass::ReleaseBuffer()
{
    m_bufferLock.Unlock();
}

Usage:

void someFunction(CSomeClass & sc)
{
    CMyBuffer & buffer = sc.LockBuffer();
    // access buffer
    sc.ReleaseBuffer();
}
  • What I like about this is, that the
    user has to make only one function
    call and can only access the buffer
    after having locked it.
  • What I don't
    like is that the user has to release
    explicitly.

Update: These additional disadvantages were pointed out by Nick Meyer and Martin York:

  • The user is able to release the lock and then use the buffer.
  • If an exception occurs before releasing the lock, the buffer remains locked.

I'd like to do it with a CSingleLock object (or something similar), which unlocks the buffer when the object goes out of scope.

How could that be done?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

最佳男配角 2024-08-06 14:17:01

一种方法是使用 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
}

One way to do this would be using 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
}
南街九尾狐 2024-08-06 14:17:01

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

#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();
}

Use an object that represents the buffer.
When this obejct is initialized get the lock and when it is destroyed release the lock.
Add a cast operator so it can be used in place of the buffer in any function call:

#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();
}
傲鸠 2024-08-06 14:17:01

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

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 对象的访问,因此它不应该在锁释放后为用户提供任何可以保留和改变的内容。

IMHO, if your goal is to prevent the user from only accessing the buffer when it is locked, you're fighting a tricky battle. Consider if the user does:

void someFunction(CSomeClass & sc)
{
   CMyBuffer & buffer = sc.LockBuffer();
   sc.ReleaseBuffer();
   buffer.someMutatingMethod(); // Whoops, accessed while unlocked!
}

In order to allow the user access to the buffer, you've got to return a reference to the buffer, which they can always make the mistake of holding on to until after the lock is released.

That said, you may be able to do something like this:

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();
      }
};

Use like:

{
   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

This forces the user to wrap the CMyBuffer object in a CMySynchronizedBuffer object in order to get at any of its mutating methods. Since it doesn't actually provide access to the underlying CMyBuffer object by returning a reference, then it shouldn't give the user anything to hang onto and mutate after the lock is released.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文