1

1つのプロセスが唯一のライターであり、多くのリーダーが存在するプロセス間通信用のクラスのペアを実装しています。1つのクラスが読み取りを処理します。1つは書き込みを処理します。他のプロセスがライターになるのを防ぐために、ライタークラスの単一のオブジェクトが必要です。このオブジェクトは、boost::named_upgradable_mutexその存続期間中、アップグレード可能なロックを保持します。boost::interprocess::upgradable_lockそのために、ライタークラスには、オブジェクトの構築時にミューテックスが渡されるタイプのメンバー変数があります。ライタープロセスが書き込むときは、ライタークラスのWrite()メソッドを呼び出します。このメソッドは、そのロックを排他的にアップグレードし、書き込みを実行し、排他ロックをアトミックに降格して、単に再度アップグレードできるようにする必要があります。

Move Semanticsを介したロック転送に関するBoostのドキュメントに従って、ライタークラスのWrite()メソッドに最初の部分(ロックを排他的にアップグレードする)を実装することができました。ただし、2番目の部分(ロックをアップグレード可能に降格)は、boost :: interprocess :: upgrade_lock型の新しいローカル変数を生成します。これは、スコープ外になり、Write()が戻ったときにミューテックスを解放します。そのアップグレード可能なロックをクラスのupgradable_lockメンバー変数に戻して、アップグレード機能がライターオブジェクトのみに残るようにする必要があります。これを行うための最良の方法は何ですか?私が思いついた唯一のことは、戻る前にローカル変数をメンバー変数と交換することです。コードは次のようになります。

using boost::interprocess;
scoped_lock<named_upgradable_mutex> my_exclusive_lock(move(m_lock));

//  do write here

upgradable_lock<named_upgradable_mutex> my_demoted_lock(move(my_exclusive_lock));
m_lock.swap(my_demoted_lock);  //  how else to do this?

これは機能しますが、その最後の行は本当に直感に反していて、考えるのに少し時間がかかりました。もっと良い方法はありますか?降格されたロックをメンバー変数に直接入れることは可能ですか?また、降格されたロックを格納するためにメンバー変数を再利用することの意図しない結果はありますか?

4

1 に答える 1

1

ムーブ代入演算子の使用を検討してください。を使用した次のコードswap()

upgradable_lock<named_upgradable_mutex> my_demoted_lock(move(my_exclusive_lock));
m_lock.swap(my_demoted_lock);

次のようになります:

m_lock = upgradable_lock<named_upgradable_mutex>(move(my_exclusive_lock));

この特定のケースでは、ムーブ代入演算子は、デフォルトの構築状態(および)であるswap()ため、副作用なしに交換可能です。m_lockm_lock.owns() == falsem_lock.mutex() == 0


アップグレード可能なロックにメンバー変数を再利用した場合の意図しない結果は考えられません。ただし、考慮すべきトピックがいくつかあります。

  • 1つの目標は、「他のプロセスがライターになるのを防ぐこと」です。コンストラクターでロックが取得されるWriterと、コードは他のプロセスがを作成するのを防ぐだけでなく、他のプロセスがを作成するのを防ぎます。その結果、ブロッキング呼び出しは、アプリケーションコードに影響を与えたり、不便にしたりする可能性があります。次のコードを検討してください。Writer

    Reader reader;
    Writer writer; // This may block, but the application code cannot react
                   // to it without dedicating an entire thread to the
                   // construction of the writer.
    

    妥協する代替策は、このコンストラクターを介してロックを取得しようとし、アプリケーションにさらに制御を提供するメンバー関数を追加することです。これにより、他のプロセスがを作成できるようになりますが、複数のプロセスが次の書き込み権限を持つことができなくなります。WriterWriter

    class Writer
    {
    public:
      bool IsPrivileged();         // Returns true if this the privileged Writer.
      bool TryBecomePrivileged();  // Non-blocking attempt to become the
                                   // privileged Writer.  Returns true on success.
      bool BecomePrivileged();     // Blocks waiting to become the privileged
                                   // Writer.  Returns true on success.
      void RelinquishPrivileges(); // We're not worthy...we're not worthy...
    
      enum Status { SUCCESS, NOT_PRIVILEGED };
      Status Write( const std::string& ); // If this is not the privileged Writer,
                                          // then attempt to become it.  If the
                                          // attempt fails, then return
                                          // NOT_PRIVILEGED.
    };
    
  • メソッド内で、「 do write hereWriter::Write() 」コードの呼び出しのいずれかが例外をスローすると、スタックがアンワインドされ、次のようになります。

    • my_exclusive_lock排他ロックを解放し、他のプロセスがアップグレード可能なロックを取得できるようにします。
    • m_lock移動中に所有権が譲渡されたときにm_lock.mutex()設定されているように、ミューテックスへのハンドルがありません。nullmy_exclusive_lock
    • をさらに呼び出すと、排他ロックを取得せずWriter::Write()に書き込みを試みます。ミューテックスへのハンドルがあったとしても、になりますので、への転送はロックを試みません。m_lockm_lock.owns()falsemy_exclusive_lock

プログラムの例を次に示します。

#include <boost/interprocess/sync/named_upgradable_mutex.hpp>
#include <boost/interprocess/sync/sharable_lock.hpp>
#include <boost/interprocess/sync/upgradable_lock.hpp>
#include <boost/move/move.hpp>
#include <iostream>

int main()
{
  namespace bip = boost::interprocess;
  typedef bip::named_upgradable_mutex mutex_t;

  struct mutex_remove
  {
    mutex_remove()  { mutex_t::remove( "example" ); }
    ~mutex_remove() { mutex_t::remove( "example" ); }
  } remover;

  // Open or create named mutex.
  mutex_t mutex( bip::open_or_create, "example" );

  // Acquire upgradable lock.
  bip::upgradable_lock< mutex_t > m_lock( mutex, bip::try_to_lock );
  std::cout << "upgradable lock own:  " << m_lock.owns()
            << " -- mutex: "            << m_lock.mutex() 
            << std::endl;

  // Acquire the exclusive lock.
  {
    std::cout << "++ Entering scope ++" << std::endl;
    std::cout << "Transferring ownership via move: Upgradable->Scoped"
              << std::endl;
    bip::scoped_lock< mutex_t > exclusive_lock( boost::move( m_lock ) );
    std::cout <<   "upgradable lock owns: " << m_lock.owns()
              << " -- mutex: "              << m_lock.mutex()
              << "\nexclusive lock owns:  " << exclusive_lock.owns() 
              << " -- mutex: "              << exclusive_lock.mutex()
              << std::endl;

    // do write here...

    // Demote lock from exclusive to just an upgradable.
    std::cout << "Transferring ownership via move: Scoped->Upgradable"
              << std::endl;
    m_lock = bip::upgradable_lock< mutex_t >( boost::move( exclusive_lock ) );
    std::cout <<   "upgradable lock owns: " << m_lock.owns()
              << " -- mutex: "              << m_lock.mutex()
              << "\nexclusive lock owns:  " << exclusive_lock.owns() 
              << " -- mutex: "              << exclusive_lock.mutex()
              << std::endl;
    std::cout << "-- Exiting scope --" << std::endl;
  }
  std::cout << "upgradable lock own:  " << m_lock.owns()
            << " -- mutex: "            << m_lock.mutex() 
            << std::endl;

  return 0;
}

これにより、次の出力が生成されます。

アップグレード可能なロック独自:1-ミューテックス:0xbff9b21c
++スコープに入る++
移動による所有権の譲渡:アップグレード可能->スコープ
アップグレード可能なロック所有:0 --mutex:0
排他ロック所有:1-ミューテックス:0xbff9b21c
移動による所有権の譲渡:スコープ->アップグレード可能
アップグレード可能なロックの所有者:1-ミューテックス:0xbff9b21c
排他ロック所有:0 --mutex:0
-スコープを終了します-
アップグレード可能なロック独自:1-ミューテックス:0xbff9b21c
于 2012-07-05T19:02:20.943 に答える