你将如何在C ++ 11中实现自己的读写器locking?
我有一套数据结构,需要使用读写器锁保护。 我知道boost :: shared_lock,但我想有一个使用std :: mutex,std :: condition_variable和/或std :: atomic的自定义实现,以便我可以更好地理解它是如何工作的(并稍后调整它) 。
每个数据结构(可移动,但不可复制)将从一个称为Commons的类inheritance,该类封装了locking。 我想公共界面看起来像这样:
class Commons { public: void read_lock(); bool try_read_lock(); void read_unlock(); void write_lock(); bool try_write_lock(); void write_unlock(); };
…所以它可以被一些公开的inheritance:
class DataStructure : public Commons {};
我正在写科学的代码,通常可以避免数据竞赛; 这个锁主要是防止我以后可能会犯的错误。 因此我的优先级是低的读取开销,所以我不会妨碍正确运行的程序太多。 每个线程都可能运行在自己的CPU内核上。
你能告诉我(伪代码是好的)读者/写作者锁? 我现在所拥有的应该是防止作家饥饿的变种。 到目前为止,我的主要问题是read_lock在检查read是否安全以实际递增读取器计数之间的差距,之后write_lock知道要等待。
void Commons::write_lock() { write_mutex.lock(); reading_mode.store(false); while(readers.load() > 0) {} } void Commons::try_read_lock() { if(reading_mode.load()) { //if another thread calls write_lock here, bad things can happen ++readers; return true; } else return false; }
我对multithreading很陌生,我真的很想理解它。 在此先感谢您的帮助!
这里是使用互斥锁和条件variables的简单读/写locking的伪代码。 互斥体API应该是不言自明的。 条件variables被认为有一个wait(Mutex&)
,其中(primefaces!)下降互斥,并等待条件被发信号。 条件是用唤醒一个服务员的signal()
或唤醒所有服务员的signal_all()
来发signal()
。
read_lock() { mutex.lock(); while (writer) unlocked.wait(mutex); readers++; mutex.unlock(); } read_unlock() { mutex.lock(); readers--; if (readers == 0) unlocked.signal_all(); mutex.unlock(); } write_lock() { mutex.lock(); while (writer || (readers > 0)) unlocked.wait(mutex); writer = true; mutex.unlock(); } write_unlock() { mutex.lock(); writer = false; unlocked.signal_all(); mutex.unlock(); }
但是这个实现有很多缺点。
只要有锁就可以唤醒所有的服务员
如果大部分服务员正在等待写锁,这是浪费 – 大多数服务员将无法获得锁,毕竟,并继续等待。 简单地使用signal()
不起作用,因为你确实想唤醒所有人,等待读锁解锁。 所以要解决这个问题,你需要单独的条件variables的可读性和可写性。
不公平。 读者饿死作家
你可以通过跟踪挂起的读写锁的数量来解决这个问题,并且一旦有挂起的写锁就停止获取读锁(尽pipe你会使读者饿死),或者随机唤醒所有的读写器或者一个写者你使用单独的条件variables,参见上面的部分)。
锁不按照他们要求的顺序处理
为了保证这一点,你需要一个真正的等待队列。 例如,您可以为每个服务员创build一个条件variables,并在释放locking之后向队列头部的所有读者或单个写入者发出信号。
即使是纯粹的读取工作负载也会由于互斥而导致争用
这个很难修复。 一种方法是使用primefaces指令来获取读或写锁(通常是比较和交换)。 如果采集失败,因为锁被采取,你将不得不倒退到互斥体。 但是,正确地做到这一点非常困难。 而且还会有争用 – primefaces指令远没有免费,特别是在有很多内核的机器上。
结论
正确实现同步原语很难 。 实现高效和公平的同步原语更加 困难 。 它几乎没有任何回报。 Linux上的pthreads,例如包含一个使用futexes和primefaces指令组合的读/写锁,因此可能会胜过几天工作中所能提供的任何东西。
请select此课程 :
// // Multi-reader Single-writer concurrency base class for Win32 // // (c) 1999-2003 by Glenn Slayden (glenn@glennslayden.com) // // #include "windows.h" class MultiReaderSingleWriter { private: CRITICAL_SECTION m_csWrite; CRITICAL_SECTION m_csReaderCount; long m_cReaders; HANDLE m_hevReadersCleared; public: MultiReaderSingleWriter() { m_cReaders = 0; InitializeCriticalSection(&m_csWrite); InitializeCriticalSection(&m_csReaderCount); m_hevReadersCleared = CreateEvent(NULL,TRUE,TRUE,NULL); } ~MultiReaderSingleWriter() { WaitForSingleObject(m_hevReadersCleared,INFINITE); CloseHandle(m_hevReadersCleared); DeleteCriticalSection(&m_csWrite); DeleteCriticalSection(&m_csReaderCount); } void EnterReader(void) { EnterCriticalSection(&m_csWrite); EnterCriticalSection(&m_csReaderCount); if (++m_cReaders == 1) ResetEvent(m_hevReadersCleared); LeaveCriticalSection(&m_csReaderCount); LeaveCriticalSection(&m_csWrite); } void LeaveReader(void) { EnterCriticalSection(&m_csReaderCount); if (--m_cReaders == 0) SetEvent(m_hevReadersCleared); LeaveCriticalSection(&m_csReaderCount); } void EnterWriter(void) { EnterCriticalSection(&m_csWrite); WaitForSingleObject(m_hevReadersCleared,INFINITE); } void LeaveWriter(void) { LeaveCriticalSection(&m_csWrite); } };
我没有机会尝试,但代码看起来不错。