2009-08-10 29 views
0

我有这两种方法的线程独占访问CMyBuffer对象:如何使用CSingleLock提供对此缓冲区的访问?

页眉:

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对象(或类似的东西)来实现,当对象超出范围时它会解锁缓冲区。

这可怎么办?

回答

1

使用表示缓冲的对象。
当此obejct初始化时,获取锁定,并在销毁时释放锁定。
添加转换运算符,因此它可以代替缓冲的任何函数调用中使用:

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

你觉得重载** operator - >()**?这样,人们可以说* b-> doStuff()*,而不必制作* static_cast *。 – foraidt 2009-08-12 11:14:41

+0

附录:我认为* static_cast *变得如此丑陋,以便使其变得引人注目并且不那么琐碎。但是在这种情况下,实际上应该这样做,所以不需要为用户做出比必要更难的施加力。 – foraidt 2009-08-12 11:25:03

+0

我目前正在尝试它,并注意到,**运算符*()**也应该超载。 – foraidt 2009-08-12 11:30:13

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对象的访问,所以它不应该让用户在释放锁之后挂起并进行变异。要做到这一点

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 
} 
+0

在这种情况下,用户可以挂在返回CMyBuffer&并继续即使在LockedBuffer使用它对象被破坏。 – 2009-08-10 15:32:39

+0

@Nick:原来的代码也可以。至少这个版本可以防止异常问题。 – 2009-08-10 16:00:19