私はC++で優れたリーダー/ライターロックを探しています。単一のまれな作家と多くの頻繁な読者のユースケースがあり、これに最適化したいと考えています。クロスプラットフォームソリューションが望ましいのですが、Windowsのみが許容されます。
boost :: thread の新しいバージョンには読み取り/書き込みロックがあります(1.35.0以降、明らかに以前のバージョンは正しく動作しませんでした)。
名前は shared_lock
、 unique_lock
、および upgrade_lock
および shared_mutex
。
標準的な事前テスト済みのビルド済みのものを使用することは常に良い方法です(たとえば、別の回答として提案されたBoost)。これは私のプロジェクトから引き出された馬鹿げた小さな実装です:
#include <pthread.h>
struct rwlock {
pthread_mutex_t lock;
pthread_cond_t read, write;
unsigned readers, writers, read_waiters, write_waiters;
};
void reader_lock(struct rwlock *self) {
pthread_mutex_lock(&self->lock);
if (self->writers || self->write_waiters) {
self->read_waiters++;
do pthread_cond_wait(&self->read, &self->lock);
while (self->writers || self->write_waiters);
self->read_waiters--;
}
self->readers++;
pthread_mutex_unlock(&self->lock);
}
void reader_unlock(struct rwlock *self) {
pthread_mutex_lock(&self->lock);
self->readers--;
if (self->write_waiters)
pthread_cond_signal(&self->write);
pthread_mutex_unlock(&self->lock);
}
void writer_lock(struct rwlock *self) {
pthread_mutex_lock(&self->lock);
if (self->readers || self->writers) {
self->write_waiters++;
do pthread_cond_wait(&self->write, &self->lock);
while (self->readers || self->writers);
self->write_waiters--;
}
self->writers = 1;
pthread_mutex_unlock(&self->lock);
}
void writer_unlock(struct rwlock *self) {
pthread_mutex_lock(&self->lock);
self->writers = 0;
if (self->write_waiters)
pthread_cond_signal(&self->write);
else if (self->read_waiters)
pthread_cond_broadcast(&self->read);
pthread_mutex_unlock(&self->lock);
}
void rwlock_init(struct rwlock *self) {
self->readers = self->writers = self->read_waiters = self->write_waiters = 0;
pthread_mutex_init(&self->lock, NULL);
pthread_cond_init(&self->read, NULL);
pthread_cond_init(&self->write, NULL);
}
pthreads
は実際にはWindowsネイティブではありませんが、一般的な考え方はここにあります。この実装は、ライターにわずかに偏っています(ライターの大群はリーダーを無期限に飢えさせることができます)。単にwriter_unlock
バランスを逆にしたい場合。
はい、これはCであり、C++ではありません。翻訳は読者に任せた演習です。
Greg Rogersは、POSIX標準ではpthread_rwlock_*
。 pthreads
がない場合、これは役に立ちませんが、次のことを思い出すようになりました: Pthreads-w32 動作するはずです!このコードを独自の使用のためにpthreads
以外に移植する代わりに、WindowsではPthreads-w32を使用し、その他の場所ではネイティブpthreads
を使用します。
C++ 17(VS2015)以降、標準を使用できます。
#include <shared_mutex>
typedef std::shared_mutex Lock;
typedef std::unique_lock< Lock > WriteLock;
typedef std::shared_lock< Lock > ReadLock;
Lock myLock;
void ReadFunction()
{
ReadLock r_lock(myLock);
//Do reader stuff
}
void WriteFunction()
{
WriteLock w_lock(myLock);
//Do writer stuff
}
古いコンパイラのバージョンと標準の場合、boostを使用して読み取り/書き込みロックを作成できます。
#include <boost/thread/locks.hpp>
#include <boost/thread/shared_mutex.hpp>
typedef boost::shared_mutex Lock;
typedef boost::unique_lock< Lock > WriteLock;
typedef boost::shared_lock< Lock > ReadLock;
使用することに決めたものは何でも、競合がない場合、読み取り/書き込みロックは単純な相互排他ロックよりも3〜40倍遅くなる傾向があるため、単純なロックに対して作業負荷をベンチマークします。
編集:MSDNマガジンのリンクはもう利用できません。 CodeProjectの記事が https://www.codeproject.com/Articles/32685/Testing-reader-writer-locks で利用できるようになり、かなりうまくまとめられています。また、 複合同期オブジェクト に関する新しいMSDNリンクを見つけました。
MSDNには、それらのいくつかの実装を提示するリーダー/ライターロックに関する 記事 があります。また、Vistaで導入されたカーネル同期プリミティブであるスリムリーダー/ライターロックも導入しています。 CodeProject記事 さまざまな実装(MSDN記事のものを含む)の比較についてもあります。
C++ 17は std::shared_mutex
。 MSVC++ 2015 および2017でサポートされています。
Intel Thread Building Blocksは、いくつかのrw_lockバリアントも提供します。
http://www.threadingbuildingblocks.org/
非常に短い競合期間にはspin_rw_mutexがあり、長い競合期間にはqueueing_rw_mutexがあります。前者は、特にパフォーマンスに敏感なコードで使用できます。後者は、Boost.Threadまたはpthreadを直接使用することにより提供されるパフォーマンスに匹敵します。しかし、どちらがあなたのアクセスパターンにとって勝利であるかを確認するためにプロファイルしてください。
ACEライブラリ をお勧めします。これは、多数のロックメカニズムを提供し、さまざまなプラットフォームに移植されます。
問題の境界条件に応じて、次のクラスが役立つことがあります。
ACE_RW_Process_Mutex
ACE_Write_Guard
およびACE_Read_Guard
ACE_Condition
Boost.Thread はリリース1.35.0以降、リーダー/ライターロックを既にサポートしています。これの良い点は、実装が非常にクロスプラットフォームであり、ピアレビューされており、実際には 今後のC++ 0x標準の参照実装 であることです。
http://www.codeproject.com/KB/threads/ReaderWriterLock.aspx
以下は、ほとんどのタスクに適した適切で軽量な実装です。
Glenn SlaydeによるWin32用のマルチリーダー、シングルライターの同期ロッククラス
Sunの優れた ReentrantReadWriteLock をコピーできます。オプションの公平性、ロックのダウングレード、そしてもちろん再入可能性などの機能が含まれています。
はい、Javaにありますが、Javaを知らなくても、簡単に読み取ってC++に置き換えることができます。私がリンクしたドキュメントには、この実装のすべての動作プロパティが含まれているため、必要な動作を確実に実行できます。
他に何もなければ、それはガイドです。