由于windows中缺less条件variables(虽然是从vista引入的,但在windows XP和2003中不支持),但在c ++中实现线程安全队列并不容易。 在Win32上实现POSIX条件variables的策略 我所要求的只是使用CriticalSection或互斥量和事件,而不使用信号和条件variables。
我也试图find一个确切的实现,只是使用Win32的本地API,但没有运气。 所以我自己完成了一个。 问题是我不是100%确定代码是线程安全的。 谁能告诉我这是好还是不好?
class CEventSyncQueue { public: CEventSyncQueue(int nCapacity = -1); virtual ~CEventSyncQueue(); virtual voID Put(voID* ptr); virtual voID* Get(); protected: int m_nCapacity; CPtrList m_List; CRITICAL_SECTION m_lock; HANDLE m_hGetEvent; HANDLE m_hPutEvent; }; CEventSyncQueue::CEventSyncQueue(int nCapacity) { m_nCapacity = nCapacity; ::InitializeCriticalSection(&m_lock); m_hPutEvent = ::CreateEvent(NulL,FALSE,NulL); m_hGetEvent = ::CreateEvent(NulL,NulL); } CEventSyncQueue::~CEventSyncQueue() { m_List.RemoveAll(); ::CloseHandle(m_hGetEvent); ::CloseHandle(m_hPutEvent); ::DeleteCriticalSection(&m_lock); } voID CEventSyncQueue::Put(voID* ptr) { ::EnterCriticalSection(&m_lock); while(m_nCapacity > 0 && m_List.GetCount() >= m_nCapacity) { ::LeaveCriticalSection(&m_lock); //wait if(::WaitForSingleObject(m_hPutEvent,INFINITE) != WAIT_OBJECT_0) { ASSERT(FALSE); } ::EnterCriticalSection(&m_lock); } if(m_nCapacity > 0) { ASSERT(m_List.GetCount() < m_nCapacity); } m_List.AddTail(ptr); ::SetEvent(m_hGetEvent); //notifyAll ::LeaveCriticalSection(&m_lock); } voID* CEventSyncQueue::Get() { ::EnterCriticalSection(&m_lock); while(m_List.IsEmpty()) { ::LeaveCriticalSection(&m_lock); //wait if(::WaitForSingleObject(m_hGetEvent,INFINITE) != WAIT_OBJECT_0) { ASSERT(FALSE); } ::EnterCriticalSection(&m_lock); } ASSERT(!m_List.IsEmpty()); voID* ptr = m_List.Removehead(); ::SetEvent(m_hPutEvent); //notifyAll ::LeaveCriticalSection(&m_lock); return ptr; }
linux的线程本地存储实现
我怎么知道一个linux系统调用是否是线程安全的?
我需要实现一种睡眠此线程的方法,直到它有工作要做
奇怪的线程NullReferenceException当读取存在的值?
应用于线程的延迟优先级更改
Openwrt允许组播stream量
一个pthreads程序的竞争条件可以完全崩溃OS或X?
一起使用gevent和multiprocessing与subprocess通信
如何让Googletesting检测linux上的线程数?
例外:bus.Bus不可用odoo 8多处理,Nginx作为代理服务器
再次想到,没有必要明确实施信号量。 相反,只要想想如何使用事件来实现信号量,并以这种方式处理您的问题。 我第一次尝试使用手动重置事件,这是效率低下,但显然是正确的,然后我优化。
请注意,我还没有调试(甚至编译!)这些代码片段,但他们应该给你正确的想法。 这里是手动重置版本:
class CEventSyncQueue { public: CEventSyncQueue(int nCapacity = -1); virtual ~CEventSyncQueue(); virtual voID Put(voID* ptr); virtual voID* Get(); protected: int m_nCapacity; CPtrList m_List; CRITICAL_SECTION m_lock; HANDLE m_queue_not_empty; HANDLE m_queue_not_full; }; CEventSyncQueue::CEventSyncQueue(int nCapacity) { m_nCapacity = nCapacity; ::InitializeCriticalSection(&m_lock); m_queue_not_empty = ::CreateEvent(NulL,TRUE,NulL); m_queue_not_full = ::CreateEvent(NulL,NulL); } CEventSyncQueue::~CEventSyncQueue() { m_List.RemoveAll(); ::CloseHandle(m_queue_not_empty); ::CloseHandle(m_queue_not_full); ::DeleteCriticalSection(&m_lock); } voID CEventSyncQueue::Put(voID* ptr) { bool done = false; while (!done) { // If the queue is full,we must wait until it isn't. if(::WaitForSingleObject(m_queue_not_full,INFINITE) != WAIT_OBJECT_0) { ASSERT(FALSE); } // However,we might not be the first to respond to the event,// so we still need to check whether the queue is full and loop // if it is. ::EnterCriticalSection(&m_lock); if (m_nCapacity <= 0 || m_List.GetCount() < m_nCapacity) { m_List.AddTail(ptr); done = true; // The queue is definitely not empty. SetEvent(m_queue_not_empty); // Check whether the queue is Now full. if (m_nCapacity > 0 && m_List.GetCount() >= m_nCapacity) { resetEvent(m_queue_not_full); } } ::LeaveCriticalSection(&m_lock); } } voID* CEventSyncQueue::Get() { voID *result = nullptr; while (result == nullptr) { // If the queue is empty,we must wait until it isn't. if(::WaitForSingleObject(m_queue_not_empty,// so we still need to check whether the queue is empty and loop // if it is. ::EnterCriticalSection(&m_lock); if (!m_List.IsEmpty()) { result = m_List.Removehead(); ASSERT(result != nullptr); // The queue shouldn't be full at this point! ASSERT(m_nCapacity <= 0 || m_List.GetCount() < m_nCapacity); SetEvent(m_queue_not_full); // Check whether the queue is Now empty. if (m_List.IsEmpty()) { resetEvent(m_queue_not_empty); } } ::LeaveCriticalSection(&m_lock); } }
以下是更高效的自动重置事件版本:
class CEventSyncQueue { public: CEventSyncQueue(int nCapacity = -1); virtual ~CEventSyncQueue(); virtual voID Put(voID* ptr); virtual voID* Get(); protected: int m_nCapacity; CPtrList m_List; CRITICAL_SECTION m_lock; HANDLE m_queue_not_empty; HANDLE m_queue_not_full; }; CEventSyncQueue::CEventSyncQueue(int nCapacity) { m_nCapacity = nCapacity; ::InitializeCriticalSection(&m_lock); m_queue_not_empty = ::CreateEvent(NulL,NulL); } CEventSyncQueue::~CEventSyncQueue() { m_List.RemoveAll(); ::CloseHandle(m_queue_not_empty); ::CloseHandle(m_queue_not_full); ::DeleteCriticalSection(&m_lock); } voID CEventSyncQueue::Put(voID* ptr) { if (m_nCapacity <= 0) { ::EnterCriticalSection(&m_lock); m_List.AddTail(ptr); SetEvent(m_queue_not_empty); ::LeaveCriticalSection(&m_lock); return; } bool done = false; while (!done) { // If the queue is full,under some (rare) conditions we'll get here and find // the queue is already full again,so be prepared to loop. ::EnterCriticalSection(&m_lock); if (m_List.GetCount() < m_nCapacity) { m_List.AddTail(ptr); done = true; SetEvent(m_queue_not_empty); if (m_List.GetCount() < m_nCapacity) { SetEvent(m_queue_not_full); } } ::LeaveCriticalSection(&m_lock); } } voID* CEventSyncQueue::Get() { voID *result = nullptr; while (result == nullptr) { // If the queue is empty,under some (rare) conditions we'll get here and find // the queue is already empty again,so be prepared to loop. ::EnterCriticalSection(&m_lock); if (!m_List.IsEmpty()) { result = m_List.Removehead(); ASSERT(result != nullptr); // The queue shouldn't be full at this point! if (m_nCapacity <= 0) ASSERT(m_List.GetCount() < m_nCapacity); SetEvent(m_queue_not_full); if (!m_List.IsEmpty()) { SetEvent(m_queue_not_empty); } } ::LeaveCriticalSection(&m_lock); } }
在windows中实现线程安全队列是微不足道的。 我用Delphi,C ++,BCB等做过
你为什么认为需要一个条件变量? 您如何看待windows消息队列的工作?
事件是用于PC队列的错误原语。 最简单/最清晰的方法是使用信号量。
简单无界的生产者 – 消费者队列。
template <typename T> class PCSqueue{ CRITICAL_SECTION access; deque<T> *objectQueue; HANDLE queueSema; public: PCSqueue(){ objectQueue=new deque<T>; InitializeCriticalSection(&access); queueSema=CreateSemaphore(NulL,MAXINT,NulL); }; voID push(T ref){ EnterCriticalSection(&access); objectQueue->push_front(ref); LeaveCriticalSection(&access); ReleaseSemaphore(queueSema,1,NulL); }; bool pop(T *ref,DWORD timeout){ if (WAIT_OBJECT_0==WaitForSingleObject(queueSema,timeout)) { EnterCriticalSection(&access); *ref=objectQueue->back(); objectQueue->pop_back(); LeaveCriticalSection(&access); return(true); } else return(false); }; };
编辑 – 一个有界的队列不会更困难 – 你需要另一个信号来计算空的空间。 我没有使用有界的队列,但是我确定它是可以的 – 一个有2个信号量和一个互斥/ CS的有界队列是标准模式。
编辑:使用PostMessage()或PostThreadMessage()API调用 – 它们被明确声明为'waveOutProc'回调是安全的。 MSDN说,调用“其他波函数”将导致死锁 – 信号调用不在该集合中,如果允许SetEvent(),但是ReleaseSemaphore()不允许,我将非常惊讶。 事实上,如果允许SetEvent(),而ReleaseSemaphore()不是在windows的任何地方,我会感到惊讶。
条件变量? 你的意思是互锁*功能? 这些已经存在了很长一段时间 – 我在windows 2000中使用它们,你可以使用它们来构建一个并发系统,但是你仍然需要自己做一些工作。
或者,尝试OpenMP。 要使用这个,你需要Visual Studio 2008或更高版本。
总结以上是内存溢出为你收集整理的win32线程安全队列实现使用本地的Windows API全部内容,希望文章能够帮你解决win32线程安全队列实现使用本地的Windows API所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)