15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту


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 | Вернуться в корень Архива |