C++ で優れたリーダー/ライター ロックを探しています。使用頻度の低い 1 人のライターと頻繁に使用する多数のリーダーのユース ケースがあり、これを最適化したいと考えています。クロスプラットフォームのソリューションが望ましいですが、Windows のみのソリューションも受け入れられます。
12 に答える
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;
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-w32が機能するはずだpthreads
ということを思い出しました。このコードを自分で使用するために non- に移植する代わりに、 Windows では Pthreads-w32 を使用し、その他の場所ではネイティブを使用してください。pthreads
pthreads
競合がない場合、読み取り/書き込みロックは単純なミューテックスよりも 3 倍から 40 倍遅くなる傾向があるため、使用するものは何でも、単純なロックに対して作業負荷のベンチマークを行います。
ここにいくつかの参照があります
編集: MSDN マガジンのリンクは利用できなくなりました。CodeProject の記事はhttps://www.codeproject.com/Articles/32685/Testing-reader-writer-locksで入手できるようになり、かなりうまくまとめられています。また、 Compound Synchronization Objectsに関する新しい MSDN リンクも見つけました。
MSDN には、リーダー/ライター ロックに関する記事があり、それらのいくつかの実装が示されています。また、Vista で導入されたカーネル同期プリミティブである Slim リーダー/ライター ロックも導入されています。さまざまな実装の比較に関する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 によって提供されるパフォーマンス、または pthreads を直接使用するパフォーマンスに匹敵します。ただし、プロファイルを作成して、どちらがアクセス パターンに適しているかを確認してください。
多数のロック メカニズムを提供し、さまざまなプラットフォームに移植されているACE ライブラリをお勧めします。
問題の境界条件によっては、次のクラスが役立つ場合があります。
ACE_RW_Process_Mutex
ACE_Write_Guard
とACE_Read_Guard
ACE_Condition
Boost.Threadは、リリース 1.35.0 以降、リーダー/ライター ロックを既にサポートしています。これの良いところは、実装が非常にクロスプラットフォームであり、査読済みであり、実際には今後の C++0x 標準のリファレンス実装であることです。
Glenn Slayde による Win32 の複数リーダー、単一ライター同期ロック クラス
Sun の優れたReentrantReadWriteLockをコピーできます。オプションの公平性、ロックのダウングレード、そしてもちろん再入可能などの機能が含まれています。
はい、Java で作成されていますが、Java をまったく知らなくても、簡単に読み取って C++ に変換できます。リンク先のドキュメントには、この実装のすべての動作プロパティが含まれているため、目的の動作を確認できます。
何もなければ、それはガイドです。