2009-08-10 53 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