SingleProducerConsumer模板类,要保护的类或者成员为模板类,读取但不修改使用ReadLock,和ReadUnlock,只修改用WriteLock和WriteUnlock.大家帮忙测试,最好给一个确切的结论呀,不要刻意在意DEBUG宏和ASSERT,代码确实不好理解。见谅,谢谢大家了!!!
#ifndef __LOCK_H__
#define __LOCK_H__
#include <stdio.h>

#define NOT_ZERO_DELETE(a) if (0 != a) {delete (void* )a;}namespace xx
{

#define MINIMUM_LIST_SIZE 8 // From RakNet If you want know some detail please visit http://www.jenkinssoftware.com/ /// \brief A single producer consumer implementation without critical sections.
template <class SingleProducerConsumerType>
class SingleProducerConsumer
{
public:
/// Constructor
SingleProducerConsumer(); /// Destructor
~SingleProducerConsumer(); /// WriteLock must be immediately followed by WriteUnlock.  These two functions must be called in the same thread.
/// \return A pointer to a block of data you can write to.
SingleProducerConsumerType* WriteLock(void); /// Call if you don't want to write to a block of data from WriteLock() after all.
/// Cancelling locks cancels all locks back up to the data passed.  So if you lock twice and cancel using the first lock, the second lock is ignored
/// \param[in] cancelToLocation Which WriteLock() to cancel.
void CancelWriteLock(SingleProducerConsumerType* cancelToLocation); /// Call when you are done writing to a block of memory returned by WriteLock()
void WriteUnlock(void); /// ReadLock must be immediately followed by ReadUnlock. These two functions must be called in the same thread.
/// \retval 0 No data is availble to read
/// \retval Non-zero The data previously written to, in another thread, by WriteLock followed by WriteUnlock.
SingleProducerConsumerType* ReadLock(void); // Cancelling locks cancels all locks back up to the data passed.  So if you lock twice and cancel using the first lock, the second lock is ignored
/// param[in] Which ReadLock() to cancel.
void CancelReadLock(SingleProducerConsumerType* cancelToLocation); /// Signals that we are done reading the the data from the least recent call of ReadLock.
/// At this point that pointer is no longer valid, and should no longer be read.
void ReadUnlock(void); /// Clear is not thread-safe and none of the lock or unlock functions should be called while it is running.
void Clear(void); /// This function will estimate how many elements are waiting to be read.  It's threadsafe enough that the value returned is stable, but not threadsafe enough to give accurate results.
/// \return An ESTIMATE of how many data elements are waiting to be read
int Size(void) const; /// Make sure that the pointer we done reading for the call to ReadUnlock is the right pointer.
/// param[in] A previous pointer returned by ReadLock()
bool CheckReadUnlockOrder(const SingleProducerConsumerType* data) const; /// Returns if ReadUnlock was called before ReadLock
/// \return If the read is locked
bool ReadIsLocked(void) const; private:
struct DataPlusPtr
{
DataPlusPtr () {readyToRead=false;}
SingleProducerConsumerType object; // Ready to read is so we can use an equality boolean comparison, in case the writePointer var is trashed while context switching.
volatile bool readyToRead;
volatile DataPlusPtr *next;
};
volatile DataPlusPtr *readAheadPointer;
volatile DataPlusPtr *writeAheadPointer;
volatile DataPlusPtr *readPointer;
volatile DataPlusPtr *writePointer;
#ifdef _DEBUG
volatile bool m_bWriteLocked;
volatile bool m_bReadLocked;
#endif
unsigned readCount, writeCount;
}; template <class SingleProducerConsumerType>
SingleProducerConsumer<SingleProducerConsumerType>::SingleProducerConsumer()
{
// Preallocate
readPointer = new DataPlusPtr;
writePointer=readPointer;
readPointer->next = new DataPlusPtr;
int listSize; assert(MINIMUM_LIST_SIZE>=3); for (listSize=2; listSize < MINIMUM_LIST_SIZE; listSize++)
{
readPointer=readPointer->next;
readPointer->next = new DataPlusPtr;
}
readPointer->next->next=writePointer; // last to next = start
readPointer=writePointer;
readAheadPointer=readPointer;
writeAheadPointer=writePointer;
readCount=writeCount=0;
} template <class SingleProducerConsumerType>
SingleProducerConsumer<SingleProducerConsumerType>::~SingleProducerConsumer()
{
volatile DataPlusPtr *next;
readPointer=writeAheadPointer->next;
while (readPointer!=writeAheadPointer)
{
next=readPointer->next;
NOT_ZERO_DELETE(readPointer);
readPointer=next;
}
NOT_ZERO_DELETE(readPointer);
} template <class SingleProducerConsumerType>
SingleProducerConsumerType* SingleProducerConsumer<SingleProducerConsumerType>::WriteLock( void )
{
#ifdef _DEBUG
if (true == m_bWriteLocked)
{
assert(false);
}
#endif
if (writeAheadPointer->next==readPointer ||
writeAheadPointer->next->readyToRead==true)
{
volatile DataPlusPtr *originalNext=writeAheadPointer->next;
writeAheadPointer->next = new DataPlusPtr;
assert(writeAheadPointer->next);
writeAheadPointer->next->next=originalNext;
} volatile DataPlusPtr *last;
last=writeAheadPointer;
writeAheadPointer=writeAheadPointer->next;
#ifdef _DEBUG
m_bWriteLocked = true;
#endif
return (SingleProducerConsumerType*) last;
} template <class SingleProducerConsumerType>
void SingleProducerConsumer<SingleProducerConsumerType>::CancelWriteLock( SingleProducerConsumerType* cancelToLocation )
{
writeAheadPointer=(DataPlusPtr *)cancelToLocation;
#ifdef _DEBUG
m_bWriteLocked = false;
#endif
} template <class SingleProducerConsumerType>
void SingleProducerConsumer<SingleProducerConsumerType>::WriteUnlock( void )
{
// DataPlusPtr *dataContainer = (DataPlusPtr *)structure; assert(writePointer->next!=readPointer);
assert(writePointer!=writeAheadPointer); writeCount++;
// User is done with the data, allow send by updating the write pointer
writePointer->readyToRead=true;
writePointer=writePointer->next;
#ifdef _DEBUG
m_bWriteLocked = false;
#endif
} template <class SingleProducerConsumerType>
SingleProducerConsumerType* SingleProducerConsumer<SingleProducerConsumerType>::ReadLock( void )
{
#ifdef _DEBUG
if (true == m_bReadLocked)
{
assert(false);
}
#endif
if (readAheadPointer==writePointer ||
readAheadPointer->readyToRead==false)
{
return 0;
}
#ifdef _DEBUG
m_bReadLocked = true;
#endif
volatile DataPlusPtr *last;
last=readAheadPointer;
readAheadPointer=readAheadPointer->next;
return (SingleProducerConsumerType*)last;
} template <class SingleProducerConsumerType>
void SingleProducerConsumer<SingleProducerConsumerType>::CancelReadLock( SingleProducerConsumerType* cancelToLocation )
{
assert(readPointer!=writePointer);
readAheadPointer=(DataPlusPtr *)cancelToLocation;
#ifdef _DEBUG
m_bReadLocked = false;
#endif
} template <class SingleProducerConsumerType>
void SingleProducerConsumer<SingleProducerConsumerType>::ReadUnlock( void )
{
assert(readAheadPointer!=readPointer); // If hits, then called ReadUnlock before ReadLock
assert(readPointer!=writePointer); // If hits, then called ReadUnlock when Read returns 0 readCount++; // Allow writes to this memory block
readPointer->readyToRead=false;
readPointer=readPointer->next;
#ifdef _DEBUG
m_bReadLocked = false;
#endif
} template <class SingleProducerConsumerType>
void SingleProducerConsumer<SingleProducerConsumerType>::Clear( void )
{
// Shrink the list down to MINIMUM_LIST_SIZE elements
volatile DataPlusPtr *next;
writePointer=readPointer->next; int listSize=1;
next=readPointer->next;
while (next!=readPointer)
{
listSize++;
next=next->next;
} while (listSize-- > MINIMUM_LIST_SIZE)
{
next=writePointer->next;
assert(writePointer!=readPointer); NOT_ZERO_DELETE(writePointer);

writePointer=next;
} readPointer->next=writePointer;
writePointer=readPointer;
readAheadPointer=readPointer;
writeAheadPointer=writePointer;
readCount=writeCount=0;
#ifdef _DEBUG
m_bReadLocked = false;
m_bWriteLocked= false;
#endif
} template <class SingleProducerConsumerType>
int SingleProducerConsumer<SingleProducerConsumerType>::Size( void ) const
{
return writeCount-readCount;
} template <class SingleProducerConsumerType>
bool SingleProducerConsumer<SingleProducerConsumerType>::CheckReadUnlockOrder(const SingleProducerConsumerType* data) const
{
return const_cast<const SingleProducerConsumerType *>(&readPointer->object) == data;
} template <class SingleProducerConsumerType>
bool SingleProducerConsumer<SingleProducerConsumerType>::ReadIsLocked(void) const
{
return readAheadPointer!=readPointer;
}}#endif

解决方案 »

  1.   

    没看见哪里有调用Windows的同步对象……
      

  2.   

    同步不一定完全需要WINDOWS的同步对象的。比如一个线程读,一个线程写,贴出来的代码对于这样的操作是线程安全的。
      

  3.   

    这完全是胡说八道
    首先线程安全你不能做这样的限制,如果是有条件的线程安全,就不是线程安全的。如果我说一个对象在同一个时刻只允许一个线程使用,那么它是不是线程安全的?当然了,又没有多线程,当然是安全的,所以6楼对线程安全的理解有很大的问题
    其次,真的一个读一个写就安全么?当然不是。一个简单的例子是
    A对象包含一个指针int * p,
    线程A这样使用它:
    int * p1 = pA->p;
    cout << *p1<<end;所有过程都是只读,pA是A的一个共享对象的指针
    另外一个线程只写:
    delete pA->p;
    pA->p = new int;
    (*pA->p) =3;当A线程把pA->p保存到int*中之后,delete pA->p发生,以后cout << *p1<<endl还是安全的么?
      

  4.   

    在另外一个版面上有个讨论,不使用系统现有的并发控制机制,能不能做一个线程安全的东西出来,我的建议是:第一这不是很可能,总有一种情况能让你的数据是不安全的,其次这种方法效率是非常低的,系统方法总是不占用CPU,是最高效的