Multi-Thread safe CObList
Mario Contestabile -- Mario_Contestabile.UOS__MTL@UOSMTL2.universal.com Thursday, March 27, 1997 >I am extending the capabilities of CObList via derivation, to allow multi-thread safe access to the list. >The trouble is that all the public access functions are NOT declared virtual, which is asking for trouble when using a derived >class :) I haven't tried it, but perhaps something like: class ThreadSafeObjectList:public CObList{ CCriticalSection m_CSect; public: GetHead() GetTail(); void RemoveAll(); }; void ThreadSafeObjectList::RemoveAll(){ CSingleLock csl(&m_CSect); CobList::RemoveAll(); } Mario Contestabile mcontest@universal.com
DFPav@aol.com Friday, March 28, 1997 In a message dated 97-03-28 15:33:01 EST, you write: > >I am extending the capabilities of CObList via derivation, to allow > multi-thread safe access to the list. > >The trouble is that all the public access functions are NOT declared > virtual, > which is asking for trouble when using a derived >class :) > How about instead of class ThreadSafeObjectList:public CObList{ CCriticalSection m_CSect; public: GetHead() GetTail(); void RemoveAll(); }; change the top to class TSOL : protected CObList { ------------- then you don't have to worry about virtual functions but you have put yourself in a whole other stew. Dan
Adrian McElligott -- aem@ezymail.com Sunday, March 30, 1997 > >I am extending the capabilities of CObList via derivation, to allow > multi-thread safe access to the list. This might help. I wrote about a year ago, it seems to work OK. > >The trouble is that all the public access functions are NOT declared > virtual, Yeah, it is a pain. Same goes for CString. If you find a better way I would like to know. Thanks // Queue.h : header file // ///////////////////////////////////////////////////////////////////////////// // CQueue window #include//class CEmailAccount; class CQueue : public CObArray { // Construction public: void Empty(); //This is not in CObArray, I added it for convenience. CQueue(); // Attributes public: CMutex m_mutex; // Operations public: // Overrides // Attributes int GetSize() const; int GetUpperBound() const; void SetSize(int nNewSize, int nGrowBy = -1); // Operations CObject* GetFromBottem(); // Clean up void FreeExtra(); void RemoveAll(); // Accessing elements CObject* GetAt(int nIndex) const; void SetAt(int nIndex, CObject* newElement); CObject*& ElementAt(int nIndex); // Direct Access to the element data (may return NULL) const CObject** GetData() const; CObject** GetData(); // Potentially growing the array void SetAtGrow(int nIndex, CObject* newElement); int Add(CObject* newElement); int Append(const CObArray& src); void Copy(const CObArray& src); // overloaded operator helpers CObject* operator[](int nIndex) const; CObject*& operator[](int nIndex); // Operations that move elements around void InsertAt(int nIndex, CObject* newElement, int nCount = 1); void RemoveAt(int nIndex, int nCount = 1); void InsertAt(int nStartIndex, CObArray* pNewArray); // Implementation public: virtual ~CQueue(); protected: }; ///////////////////////////////////////////////////////////////////////////// // Queue.cpp : implementation file // #include "stdafx.h" #include #include "Queue.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CQueue CQueue::CQueue() { } CQueue::~CQueue() { Empty(); } ///////////////////////////////////////////////////////////////////////////// int CQueue::GetSize() const { CSingleLock sLock((CSyncObject*) &m_mutex); sLock.Lock(); //Lock() will wait INFINITE return CObArray::GetSize(); } ///////////////////////////////////////////////////////////////////////////// int CQueue::GetUpperBound() const { CSingleLock sLock((CSyncObject*)&m_mutex); sLock.Lock(); //Lock() will wait INFINITE return CObArray::GetUpperBound(); } ///////////////////////////////////////////////////////////////////////////// void CQueue::SetSize(int nNewSize, int nGrowBy) { CSingleLock sLock(&m_mutex); sLock.Lock(); //Lock() will wait INFINITE CObArray::SetSize(nNewSize, nGrowBy); } ///////////////////////////////////////////////////////////////////////////// void CQueue::FreeExtra() { CSingleLock sLock(&m_mutex); sLock.Lock(); //Lock() will wait INFINITE CObArray::FreeExtra(); } ///////////////////////////////////////////////////////////////////////////// void CQueue::RemoveAll() { CSingleLock sLock(&m_mutex); sLock.Lock(); //Lock() will wait INFINITE CObArray::RemoveAll(); } ///////////////////////////////////////////////////////////////////////////// CObject* CQueue::GetAt(int nIndex) const { CSingleLock sLock((CSyncObject*)&m_mutex); sLock.Lock(); //Lock() will wait INFINITE return CObArray::GetAt(nIndex); } ///////////////////////////////////////////////////////////////////////////// void CQueue::SetAt(int nIndex, CObject* newElement) { CSingleLock sLock(&m_mutex); sLock.Lock(); //Lock() will wait INFINITE CObArray::SetAt(nIndex, newElement); } ///////////////////////////////////////////////////////////////////////////// CObject*& CQueue::ElementAt(int nIndex) { CSingleLock sLock(&m_mutex); sLock.Lock(); //Lock() will wait INFINITE return CObArray::ElementAt(nIndex); } ///////////////////////////////////////////////////////////////////////////// const CObject** CQueue::GetData() const { CSingleLock sLock((CSyncObject*)&m_mutex); sLock.Lock(); //Lock() will wait INFINITE return CObArray::GetData(); } ///////////////////////////////////////////////////////////////////////////// CObject** CQueue::GetData() { CSingleLock sLock(&m_mutex); sLock.Lock(); //Lock() will wait INFINITE return CObArray::GetData(); } ///////////////////////////////////////////////////////////////////////////// void CQueue::SetAtGrow(int nIndex, CObject* newElement) { CSingleLock sLock(&m_mutex); sLock.Lock(); //Lock() will wait INFINITE CObArray::SetAtGrow(nIndex, newElement); } ///////////////////////////////////////////////////////////////////////////// int CQueue::Add(CObject* newElement) { CSingleLock sLock(&m_mutex); sLock.Lock(); //Lock() will wait INFINITE return CObArray::Add(newElement); } ///////////////////////////////////////////////////////////////////////////// int CQueue::Append(const CObArray& src) { CSingleLock sLock(&m_mutex); sLock.Lock(); //Lock() will wait INFINITE return CObArray::Append(src); } ///////////////////////////////////////////////////////////////////////////// void CQueue::Copy(const CObArray& src) { CSingleLock sLock(&m_mutex); sLock.Lock(); //Lock() will wait INFINITE CObArray::Copy(src); } ///////////////////////////////////////////////////////////////////////////// CObject* CQueue::operator[](int nIndex) const { CSingleLock sLock((CSyncObject*)&m_mutex); sLock.Lock(); //Lock() will wait INFINITE return CObArray::operator[](nIndex); } ///////////////////////////////////////////////////////////////////////////// CObject*& CQueue::operator[](int nIndex) { CSingleLock sLock(&m_mutex); sLock.Lock(); //Lock() will wait INFINITE return CObArray::operator[](nIndex); } ///////////////////////////////////////////////////////////////////////////// void CQueue::InsertAt(int nIndex, CObject* newElement, int nCount) { CSingleLock sLock(&m_mutex); sLock.Lock(); //Lock() will wait INFINITE CObArray::InsertAt(nIndex, newElement, nCount); } ///////////////////////////////////////////////////////////////////////////// void CQueue::RemoveAt(int nIndex, int nCount ) { CSingleLock sLock(&m_mutex); sLock.Lock(); //Lock() will wait INFINITE CObArray::RemoveAt( nIndex, nCount ); } ///////////////////////////////////////////////////////////////////////////// void CQueue::InsertAt(int nStartIndex, CObArray* pNewArray) { CSingleLock sLock(&m_mutex); sLock.Lock(); //Lock() will wait INFINITE CObArray::InsertAt(nStartIndex, pNewArray); } ///////////////////////////////////////////////////////////////////////////// CObject* CQueue::GetFromBottem() { CSingleLock sLock(&m_mutex); sLock.Lock(); //Lock() will wait INFINITE if(CObArray::GetSize()==0) { return NULL; } CObject* pObject=CObArray::GetAt(0); CObArray::RemoveAt(0); return pObject; } ///////////////////////////////////////////////////////////////////////////// void CQueue::Empty() { CSingleLock sLock(&m_mutex); sLock.Lock(); //Lock() will wait INFINITE CObject* pObject; while(CObArray::GetSize()!=0) { pObject=CObArray::GetAt(0); delete pObject; CObArray::RemoveAt(0); } } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ---------- From: DFPav@aol.com[SMTP:DFPav@aol.com] Sent: Saturday, 29 March 1997 8:02 To: mfc-l@netcom.com Subject: Re: Multi-Thread safe CObList In a message dated 97-03-28 15:33:01 EST, you write: > >I am extending the capabilities of CObList via derivation, to allow > multi-thread safe access to the list. > >The trouble is that all the public access functions are NOT declared > virtual, > which is asking for trouble when using a derived >class :) > How about instead of class ThreadSafeObjectList:public CObList{ CCriticalSection m_CSect; public: GetHead() GetTail(); void RemoveAll(); }; change the top to class TSOL : protected CObList { ------------- then you don't have to worry about virtual functions but you have put yourself in a whole other stew. Dan
Daniel W. Levi -- DLevi@hworks.com Monday, March 31, 1997 Adrian's solution, deriving from CObjList (below) will give you a threadsafe CObjList, as long as you don't do this: CQueue cq; CObList* pCOL = &cq; // accessing the list through this pointer is not thread-safe! // base class functions will be called! pCOL->GetSize(); // bypasses thread locking A safer alternative is to use a CObList data mem, instead of deriving. Since you never want to use the base functions directly, this should be better. Adrian's header file becomes >snip< class CQueue : public CObArray { // Construction public: void Empty(); //This is not in CObArray, I added it for convenience. CQueue(); // Attributes public: CMutex m_mutex; CObList m_list; /// added by DWL // Operations public:and his implementations become >snip< //////////////////////////////////////////////////////////////////////////// / int CQueue::GetSize() const { CSingleLock sLock((CSyncObject*) &m_mutex); sLock.Lock(); //Lock() will wait INFINITE return m_list.GetSize(); } Now if you're trying to be slick, and you want to bypass the locking when you know its safe, you can use derivation and a CObList*. Not that I think you'd get much of a performance boost. Alternatively, you could make the CObList member public, and bypass the locking that way. Actually, maybe you would get a boost. Maybe thats why CObList wasn't thread-safe to start with. Dan Levi ---------- > From: Adrian McElligott > To: 'mfc-l@netcom.com' > Subject: RE: Multi-Thread safe CObList > Date: Sunday, March 30, 1997 7:28 AM > > > >I am extending the capabilities of CObList via derivation, to allow > > multi-thread safe access to the list. > This might help. I wrote about a year ago, it seems to work OK. > > >The trouble is that all the public access functions are NOT declared > > virtual, > Yeah, it is a pain. Same goes for CString. > If you find a better way I would like to know. Thanks > > // Queue.h : header file > // > > //////////////////////////////////////////////////////////////////////////// / > // CQueue window > #include > //class CEmailAccount; > class CQueue : public CObArray > { > // Construction > public: > void Empty(); //This is not in CObArray, I added it for convenience. > CQueue(); > > // Attributes > public: > CMutex m_mutex; > // Operations > public: > > // Overrides > // Attributes > int GetSize() const; > int GetUpperBound() const; > void SetSize(int nNewSize, int nGrowBy = -1); > > // Operations > CObject* GetFromBottem(); > // Clean up > void FreeExtra(); > void RemoveAll(); > > // Accessing elements > CObject* GetAt(int nIndex) const; > void SetAt(int nIndex, CObject* newElement); > CObject*& ElementAt(int nIndex); > > // Direct Access to the element data (may return NULL) > const CObject** GetData() const; > CObject** GetData(); > > // Potentially growing the array > void SetAtGrow(int nIndex, CObject* newElement); > int Add(CObject* newElement); > int Append(const CObArray& src); > void Copy(const CObArray& src); > > // overloaded operator helpers > CObject* operator[](int nIndex) const; > CObject*& operator[](int nIndex); > > // Operations that move elements around > void InsertAt(int nIndex, CObject* newElement, int nCount = 1); > void RemoveAt(int nIndex, int nCount = 1); > void InsertAt(int nStartIndex, CObArray* pNewArray); > > // Implementation > public: > virtual ~CQueue(); > > protected: > }; > > //////////////////////////////////////////////////////////////////////////// / > > > > > > // Queue.cpp : implementation file > // > > #include "stdafx.h" > #include > > #include "Queue.h" > > #ifdef _DEBUG > #define new DEBUG_NEW > #undef THIS_FILE > static char THIS_FILE[] = __FILE__; > #endif > > //////////////////////////////////////////////////////////////////////////// / > // CQueue > > CQueue::CQueue() > { > } > > CQueue::~CQueue() > { > Empty(); > } > > //////////////////////////////////////////////////////////////////////////// / > int CQueue::GetSize() const > { > CSingleLock sLock((CSyncObject*) &m_mutex); > sLock.Lock(); //Lock() will wait INFINITE > return CObArray::GetSize(); > } > //////////////////////////////////////////////////////////////////////////// / > int CQueue::GetUpperBound() const > { > CSingleLock sLock((CSyncObject*)&m_mutex); > sLock.Lock(); //Lock() will wait INFINITE > return CObArray::GetUpperBound(); > } > //////////////////////////////////////////////////////////////////////////// / > void CQueue::SetSize(int nNewSize, int nGrowBy) > { > CSingleLock sLock(&m_mutex); > sLock.Lock(); //Lock() will wait INFINITE > CObArray::SetSize(nNewSize, nGrowBy); > } > //////////////////////////////////////////////////////////////////////////// / > void CQueue::FreeExtra() > { > CSingleLock sLock(&m_mutex); > sLock.Lock(); //Lock() will wait INFINITE > CObArray::FreeExtra(); > } > //////////////////////////////////////////////////////////////////////////// / > void CQueue::RemoveAll() > { > CSingleLock sLock(&m_mutex); > sLock.Lock(); //Lock() will wait INFINITE > CObArray::RemoveAll(); > } > //////////////////////////////////////////////////////////////////////////// / > CObject* CQueue::GetAt(int nIndex) const > { > CSingleLock sLock((CSyncObject*)&m_mutex); > sLock.Lock(); //Lock() will wait INFINITE > return CObArray::GetAt(nIndex); > } > //////////////////////////////////////////////////////////////////////////// / > void CQueue::SetAt(int nIndex, CObject* newElement) > { > CSingleLock sLock(&m_mutex); > sLock.Lock(); //Lock() will wait INFINITE > CObArray::SetAt(nIndex, newElement); > } > //////////////////////////////////////////////////////////////////////////// / > CObject*& CQueue::ElementAt(int nIndex) > { > CSingleLock sLock(&m_mutex); > sLock.Lock(); //Lock() will wait INFINITE > return CObArray::ElementAt(nIndex); > } > //////////////////////////////////////////////////////////////////////////// / > const CObject** CQueue::GetData() const > { > CSingleLock sLock((CSyncObject*)&m_mutex); > sLock.Lock(); //Lock() will wait INFINITE > return CObArray::GetData(); > } > //////////////////////////////////////////////////////////////////////////// / > CObject** CQueue::GetData() > { > CSingleLock sLock(&m_mutex); > sLock.Lock(); //Lock() will wait INFINITE > return CObArray::GetData(); > } > //////////////////////////////////////////////////////////////////////////// / > void CQueue::SetAtGrow(int nIndex, CObject* newElement) > { > CSingleLock sLock(&m_mutex); > sLock.Lock(); //Lock() will wait INFINITE > CObArray::SetAtGrow(nIndex, newElement); > } > //////////////////////////////////////////////////////////////////////////// / > int CQueue::Add(CObject* newElement) > { > CSingleLock sLock(&m_mutex); > sLock.Lock(); //Lock() will wait INFINITE > return CObArray::Add(newElement); > } > //////////////////////////////////////////////////////////////////////////// / > int CQueue::Append(const CObArray& src) > { > CSingleLock sLock(&m_mutex); > sLock.Lock(); //Lock() will wait INFINITE > return CObArray::Append(src); > } > //////////////////////////////////////////////////////////////////////////// / > void CQueue::Copy(const CObArray& src) > { > CSingleLock sLock(&m_mutex); > sLock.Lock(); //Lock() will wait INFINITE > CObArray::Copy(src); > } > //////////////////////////////////////////////////////////////////////////// / > CObject* CQueue::operator[](int nIndex) const > { > CSingleLock sLock((CSyncObject*)&m_mutex); > sLock.Lock(); //Lock() will wait INFINITE > return CObArray::operator[](nIndex); > } > //////////////////////////////////////////////////////////////////////////// / > CObject*& CQueue::operator[](int nIndex) > { > CSingleLock sLock(&m_mutex); > sLock.Lock(); //Lock() will wait INFINITE > return CObArray::operator[](nIndex); > } > //////////////////////////////////////////////////////////////////////////// / > void CQueue::InsertAt(int nIndex, CObject* newElement, int nCount) > { > CSingleLock sLock(&m_mutex); > sLock.Lock(); //Lock() will wait INFINITE > CObArray::InsertAt(nIndex, newElement, nCount); > } > //////////////////////////////////////////////////////////////////////////// / > void CQueue::RemoveAt(int nIndex, int nCount ) > { > CSingleLock sLock(&m_mutex); > sLock.Lock(); //Lock() will wait INFINITE > CObArray::RemoveAt( nIndex, nCount ); > } > //////////////////////////////////////////////////////////////////////////// / > void CQueue::InsertAt(int nStartIndex, CObArray* pNewArray) > { > CSingleLock sLock(&m_mutex); > sLock.Lock(); //Lock() will wait INFINITE > CObArray::InsertAt(nStartIndex, pNewArray); > } > //////////////////////////////////////////////////////////////////////////// / > CObject* CQueue::GetFromBottem() > { > CSingleLock sLock(&m_mutex); > sLock.Lock(); //Lock() will wait INFINITE > if(CObArray::GetSize()==0) > { > return NULL; > } > CObject* pObject=CObArray::GetAt(0); > CObArray::RemoveAt(0); > return pObject; > } > //////////////////////////////////////////////////////////////////////////// / > void CQueue::Empty() > { > CSingleLock sLock(&m_mutex); > sLock.Lock(); //Lock() will wait INFINITE > CObject* pObject; > while(CObArray::GetSize()!=0) > { > pObject=CObArray::GetAt(0); > delete pObject; > CObArray::RemoveAt(0); > } > } > //////////////////////////////////////////////////////////////////////////// / > //////////////////////////////////////////////////////////////////////////// / > > > ---------- > From: DFPav@aol.com[SMTP:DFPav@aol.com] > Sent: Saturday, 29 March 1997 8:02 > To: mfc-l@netcom.com > Subject: Re: Multi-Thread safe CObList > > In a message dated 97-03-28 15:33:01 EST, you write: > > > >I am extending the capabilities of CObList via derivation, to allow > > multi-thread safe access to the list. > > >The trouble is that all the public access functions are NOT declared > > virtual, > > which is asking for trouble when using a derived >class :) > > > How about instead of > class ThreadSafeObjectList:public CObList{ > CCriticalSection m_CSect; > public: > GetHead() > GetTail(); > void RemoveAll(); > }; > > change the top to class TSOL : protected CObList { > ------------- > then you don't have to worry about virtual functions but you > have put yourself in a whole other stew. > Dan >
Mike Blaszczak -- mikeblas@nwlink.com Monday, March 31, 1997 At 13:57 3/31/97 -0500, Daniel W. Levi wrote: >Now if you're trying to be slick, and you want to bypass the locking when >you know its safe, you can use derivation and a CObList*. Not that I think >you'd get much of a performance boost. Alternatively, you could make the >CObList member public, and bypass the locking that way. > >Actually, maybe you would get a boost. Maybe thats why CObList wasn't >thread-safe to start with. That's exactly why it wasn't (or any of the other MFC collections aren't) thread-safe to begin with. Entering and exiting a critical section isn't very cheap, though it's quite faster than using a CMutex like you're using. Since very, very few applications need thread safe collections, the class doesn't include it inherently. And, in situations where you _do_ need thread safety, you're probably combining many different objects together to make something interesting. As such, it might be redundant to give the collection classes thread-safety--since you'll probably have some higher-level objects gating your access instead of relying on the synchronization whic could be builtin to the class, you're truly wasting time in the collection's synchronization. Further, it might be dangerous to give the collection classes thread-safety. It's conceivable that code which accesses more than one collection class could end up deadlocking because it blocks on the access to another resource that's in use by another thread which is waiting for access to the first object. It's yet another argument, but this is where the idealistic notions of encapsulation that some people preach breaks down: if you don't know the performance characteristics or thread-blocking characteristics of a particular class, you'll find the class very hard to use. But the encapsulation-fevered pundits of object-oriented design blissfully ignore such real world issues. .B ekiM http://www.nwlink.com/~mikeblas/ These words are my own. I do not speak on behalf of Microsoft. One is too many and a million is not enough.
Become an MFC-L member | Вернуться в корень Архива |