10

ある銀行口座から別の銀行口座に送金するという古典的な問題では、受け入れられている解決策 (私は信じています) は、ミューテックスを各銀行口座に関連付けてから、一方の口座からお金を引き出して他方の口座に入金する前に両方をロックすることです。一見すると、私は次のようにします。

class Account {
public:
  void deposit(const Money& amount);
  void withdraw(const Money& amount);
  void lock() { m.lock(); }
  void unlock() { m.unlock(); }

private:
  std::mutex m;
};

void transfer(Account& src, Account& dest, const Money& amount)
{
  src.lock();
  dest.lock();

  src.withdraw(amount);
  dest.deposit(amount);

  dest.unlock();
  src.unlock();
}

しかし、手動ロック解除は臭いです。ミューテックスをパブリックにしてからstd::lock_guardintransferで使用することもできますが、パブリック データ メンバーも臭いがします。

の要件std::lock_guardは、その型がBasicLockableの要件を満たしていることです。これは、呼び出しが有効lockであることだけです。その要件を満たしているので、直接使用できます。unlockAccountstd::lock_guardAccount

void transfer(Account& src, Account& dest, const Money& amount)
{
  std::lock_guard<Account> g1(src);
  std::lock_guard<Account> g2(dest);

  src.withdraw(amount);
  dest.deposit(amount);
}

これは問題ないように思えますが、この種のことをこれまでに見たことがなく、ミューテックスのロックとロック解除を複製することAccount自体がちょっと臭いようです。

このようなシナリオで保護しているデータにミューテックスを関連付ける最良の方法は何ですか?

更新: 以下のコメントで、std::lockデッドロックを回避するために を使用できることを指摘しましたが、( および の機能に加えて) 機能の存在に依存していることを見落としていましstd::lockた。のインターフェースに追加することは、かなりひどいハックのようです。したがって、オブジェクトのミューテックスを に残すには、公開する必要があるようです。これはかなりの悪臭を放っています。try_locklockunlocktry_lockAccountAccountAccount

いくつかの提案された解決策では、クライアントがラッパー クラスを使用してミューテックスをAccountオブジェクトに静かに関連付けますが、私のコメントで述べたように、これにより、コードのさまざまな部分でさまざまなラッパー オブジェクトを使用しAccount、それぞれが独自のオブジェクトを作成することが容易になるようです。これは、コードのさまざまな部分がさまざまなミューテックスを使用してロックしようとする可能性があることを意味しますAccount。良くないね。

他の提案されたソリューションは、一度に 1 つのミューテックスのみをロックすることに依存しています。これにより、複数のミューテックスをロックする必要がなくなりますが、一部のスレッドがシステムの一貫性のないビューを参照できるようになります。本質的に、これは複数のオブジェクトを含む操作のトランザクション セマンティクスを放棄します。

この時点で、パブリック ミューテックスは利用可能なオプションの中で最も悪臭が少ないように見え始めており、それは私が本当に言いたくない結論です。本当に良いものはありませんか?

4

8 に答える 8

0

ほとんどのソリューションには、データが公開されているため、ロックを解除せずにアクセスできるという問題があります。

これを修正する方法はありますが、テンプレートを使用できないため、マクロに頼る必要があります。C++11 で実装する方がはるかに優れており、ここで議論全体を繰り返すよりも、 https ://github.com/sveljko/lockstrap の実装にリンクしています。

于 2014-11-10T04:25:21.273 に答える