いくつかのデータを頻繁に読み取る必要があるマルチスレッドアプリがあり、そのデータが更新される場合があります。現時点では、ミューテックスはそのデータへのアクセスを安全に保ちますが、複数のスレッドに同時に読み取りを許可し、更新が必要な場合にのみロックアウトするため、高価です(更新スレッドは他のスレッドの終了を待つことができます) 。
これがboost::shared_mutex
はそうすることになっていますが、私はそれをどのように使用するか明確ではなく、明確な例を見つけていません。
誰かが私が始めるために使用できる簡単な例を持っていますか?
このようなことをするように見えます:
boost::shared_mutex _access;
void reader()
{
// get shared access
boost::shared_lock<boost::shared_mutex> lock(_access);
// now we have shared access
}
void writer()
{
// get upgradable access
boost::upgrade_lock<boost::shared_mutex> lock(_access);
// get exclusive access
boost::upgrade_to_unique_lock<boost::shared_mutex> uniqueLock(lock);
// now we have exclusive access
}
1800情報は多かれ少なかれ正しいですが、修正したい問題がいくつかあります。
boost::shared_mutex _access;
void reader()
{
boost::shared_lock< boost::shared_mutex > lock(_access);
// do work here, without anyone having exclusive access
}
void conditional_writer()
{
boost::upgrade_lock< boost::shared_mutex > lock(_access);
// do work here, without anyone having exclusive access
if (something) {
boost::upgrade_to_unique_lock< boost::shared_mutex > uniqueLock(lock);
// do work here, but now you have exclusive access
}
// do more work here, without anyone having exclusive access
}
void unconditional_writer()
{
boost::unique_lock< boost::shared_mutex > lock(_access);
// do work here, with exclusive access
}
また、shared_lockとは異なり、アップグレードされていない場合でも、一度にupgrade_lockを取得できるのは1つのスレッドのみです(これに遭遇したとき、それは厄介だと思いました)。したがって、すべてのリーダーが条件付きライターである場合、別のソリューションを見つける必要があります。
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
}
古いバージョンでは、同じ構文でブーストを使用できます。
#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 shared_mutex(複数読み取り/書き込み)の例は? は、アップグレードされていない場合でも1つのスレッドのみがupgrade_lockを持つことができるという重要な情報を追加する良い答えです。これは、共有ロックを最初に解除しないと共有ロックから一意のロックにアップグレードできないことを意味します。 (これは他の場所で議論されていますが、最も興味深いスレッドはこちらです http://thread.gmane.org/gmane.comp.lib.boost.devel/214394 )
ただし、ロックへのアップグレードを待機しているスレッド(つまり、すべてのリーダーが共有ロックを解除するのを待機する必要があるスレッド)と同じことを待機しているライターロック(unique_lock)の間に重要な(文書化されていない)違いが見つかりました。
Shared_mutexのunique_lockを待っているスレッドは、入ってくる新しいリーダーをブロックし、ライターのリクエストを待つ必要があります。これにより、読者が作家を飢えさせないようにします(ただし、作家は読者を飢えさせることができると信じています)。
Upgradeable_lockのアップグレードを待機しているスレッドは、他のスレッドが共有ロックを取得できるようにするため、リーダーが非常に頻繁な場合、このスレッドは枯渇する可能性があります。
これは考慮すべき重要な問題であり、おそらく文書化する必要があります。
リーダーの数に等しいカウントでセマフォを使用します。各リーダーが読み取るためにセマフォの1カウントを取得できるようにします。これにより、すべてのリーダーが同時に読み取ることができます。次に、ライターが書き込みの前にすべてのセマフォカウントを取得できるようにします。これにより、ライターはすべての読み取りが完了するまで待機し、書き込み中に読み取りをブロックします。
ジム・モリスによる素晴らしい反応、私はこれにつまずきました、そして、それを理解するのに私はしばらく時間がかかりました。次に、unique_lockブースト(バージョン1.54)の「リクエスト」を送信した後、すべてのshared_lockリクエストをブロックすることを示す簡単なコードを示します。これは非常に興味深いものです。unique_lockとupgradeable_lockのどちらかを選択すると、書き込みの優先度を設定するか、優先度を設定しないかを選択できるように思えます。
また、(1)Jim Morrisの投稿では、これと矛盾しているようです。 Boost shared_lock。Read preferred?
#include <iostream>
#include <boost/thread.hpp>
using namespace std;
typedef boost::shared_mutex Lock;
typedef boost::unique_lock< Lock > UniqueLock;
typedef boost::shared_lock< Lock > SharedLock;
Lock tempLock;
void main2() {
cout << "10" << endl;
UniqueLock lock2(tempLock); // (2) queue for a unique lock
cout << "11" << endl;
boost::this_thread::sleep(boost::posix_time::seconds(1));
lock2.unlock();
}
void main() {
cout << "1" << endl;
SharedLock lock1(tempLock); // (1) aquire a shared lock
cout << "2" << endl;
boost::thread tempThread(main2);
cout << "3" << endl;
boost::this_thread::sleep(boost::posix_time::seconds(3));
cout << "4" << endl;
SharedLock lock3(tempLock); // (3) try getting antoher shared lock, deadlock here
cout << "5" << endl;
lock1.unlock();
lock3.unlock();
}