如何使用 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 移动/调制音频缓冲频率