ミューテックスがロックされているかどうかを確認できないのはなぜですか?


28

C ++ 14は、std::mutexがロックされているかどうかをチェックするメカニズムを省略しているようです。このSOの質問を参照してください。

https://stackoverflow.com/questions/21892934/how-to-assert-if-a-stdmutex-is-locked

これを回避するには、いくつかの方法があります。

std::mutex::try_lock()
std::unique_lock::owns_lock()

しかし、これらのどちらも特に満足のいく解決策ではありません。

try_lock()現在のスレッドがミューテックスをロックしている場合、false negativeを返すことが許可され、未定義の動作があります。また、副作用もあります。オリジナルの上のowns_lock()建設を必要とします。unique_lockstd::mutex

明らかに、自分でロールバックすることもできますが、現在のインターフェースの動機を理解したいです。

ミューテックスのステータスをチェックする機能(std::mutex::is_locked())は、私には難解な要求とは思えないので、標準委員会は見落としではなく意図的にこの機能を省略していると思います。

どうして?

編集:わかりましたので、多分このユースケースは私が期待していたほど一般的ではないので、特定のシナリオを説明します。複数のスレッドに分散された機械学習アルゴリズムがあります。各スレッドは非同期で動作し、最適化の問題が完了するとマスタープールに戻ります。

次に、マスターミューテックスをロックします。次に、スレッドは子孫を変異させる新しい親を選択する必要がありますが、現在他のスレッドによって最適化されている子孫を持たない親からのみ選択できます。したがって、現在別のスレッドによってロックされていない親を見つけるために検索を実行する必要があります。マスタースレッドミューテックスがロックされているため、検索中にミューテックスのステータスが変更されるリスクはありません。明らかに他の解決策もありますが(私は現在ブール値のフラグを使用しています)、mutexはスレッド間同期の目的で存在するこの問題に対する論理的な解決策を提供すると考えました。


42
ミューテックスがロックされているかどうかを実際に合理的にチェックすることはできません。なぜなら、チェック後1ナノ秒でロック解除またはロックされるからです。したがって、「if(mutex_is_locked())...」と書いた場合、mutex_is_lockedは正しい結果を返すことができますが、「if」が実行されるまでに間違っています。
gnasher729

1
これ^。どのような有用な情報を入手できis_lockedますか?
役に立たない

3
これはXY問題のように感じます。子が生成されている間だけ親の再利用を防止しようとするのはなぜですか?どの親にも子孫が1人しかいないという要件がありますか?あなたのロックはそれを防ぎません。明確な世代がいないのですか?そうでない場合は、より頻繁に/より早く選択できるため、より速く最適化できる個人がより高いフィットネスを持っていることを知っていますか?世代を使用する場合、すべての親を前もって選択してから、スレッドがキューから親を取得できるようにしませんか?子孫の生成は本当に高価であるため、複数のスレッドが必要ですか?
アモン

10
@quant-サンプルアプリケーションの親オブジェクトのミューテックスがミューテックスである必要がある理由がわかりません。設定されるたびにロックされるマスターミューテックスがある場合、ブール変数を使用してそれらのステータスを示すことができます。
ペリアタブレアッタ

4
私は質問の最後の文に同意しません。ここでは、単純なブール値はミューテックスよりもはるかにクリーンです。親を「返す」ためにマスターミューテックスをロックしたくない場合は、アトミックブールにします。
セバスチャンレッド

回答:


53

推奨される操作には少なくとも2つの重大な問題があります。

最初のものは@ gnasher729のコメントですでに言及されています

ミューテックスがロックされているかどうかを実際に合理的にチェックすることはできません。なぜなら、チェック後1ナノ秒でロック解除またはロックされる可能性があるからです。あなたが書いた場合、正しい結果を返すことができますがif (mutex_is_locked ()) …、実行されるまでに間違っています。mutex_is_lockedif

ミューテックスの「現在ロックされている」プロパティが変更されないことを確認する唯一の方法は、自分でロックすることです。

2つ目の問題は、mutexをロックしない限り、スレッドが以前にmutexをロックしていたスレッドと同期しないことです。したがって、「前」と「後」について話すことさえ明確に定義されておらず、ミューテックスがロックされているかどうかは、シュレディガーの猫が現在ボックスを開こうとせずに生きているかどうかを尋ねるようなものです。

私が正しく理解していれば、ロックされているマスターミューテックスのおかげで、あなたの特定のケースでは両方の問題は意味がありません。しかし、これは私にとって特に一般的なケースとは思えないので、委員会は、非常に特別なシナリオでいくらか有用で、他のすべてに損害を与える機能を追加しないことで正しいことをしたと思います。(「インターフェースを正しく使いやすく、誤って使いにくいようにする」という精神で。)

そして私が言うかもしれない場合、私はあなたが現在持っているセットアップは最もエレガントではなく、問題を完全に回避するためにリファクタリングできると思います。たとえば、現在ロックされていない親のすべての潜在的な親をマスタースレッドでチェックする代わりに、準備ができた親のキューを維持してみませんか?スレッドが別のスレッドを最適化する場合は、キューから次のスレッドをポップし、新しい親が作成されるとすぐにそれらをキューに追加します。そうすれば、コーディネーターとしてマスタースレッドさえ必要ありません。


ありがとう、これは良い答えです。準備が整った親のキューを維持したくないのは、親が作成された順序を保持する必要があるためです(これにより、寿命が決まります)。これは、LIFOキューを使用して簡単に実行できます。物事を出し入れし始めると、物事を複雑にする別の順序付けメカニズムを維持する必要があります。したがって、現在のアプローチです。
quant

14
@quant:親をキューに入れる2つの目的がある場合は、2つのキューでそれを行うことができます

@quant:アイテムを(最大で)1回削除しますが、おそらくそれぞれ複数回処理するため、一般的なケースを犠牲にしてまれなケースを最適化しています。これはめったに望ましいことではありません。
ジェリーコフィン

2
しかし、現在のスレッドがミューテックスをロックしているかどうかを尋ねること合理的です。
限定A罪

@LimitedAtonementそうでもありません。これを行うには、ミューテックスは追加情報(スレッドID)を保存する必要があるため、処理が遅くなります。再帰的なmutexはすでにこれを行っているので、代わりに使用する必要があります。
-StaceyGirl

9

最適化問題へのアクセスをロックするのではなく、最適化問題が現在最適化されているかどうかを判断するために、セカンダリミューテックスを使用しているようです。

それは完全に不要です。最適化が必要な問題のリスト、現在最適化されている問題のリスト、最適化された問題のリストがあります。(文字通り「リスト」をとるのではなく、「任意の適切なデータ構造」を意味すると考えてください)。

最適化されていない問題のリストに新しい問題を追加したり、あるリストから次のリストに問題を移動したりする操作は、単一の「マスター」ミューテックスの保護下で行われます。


1
型のオブジェクトがstd::mutexそのようなデータ構造に適しているとは思わないのですか?
quant

2
@quant-いいえ。 std::mutexオペレーティングシステムで定義されたmutex実装に依存します。この実装は、制限され、割り当てや操作が遅いリソース(ハンドルなど)を十分に使用する場合があります。単一のミューテックスを使用して内部データ構造へのアクセスをロックする方がはるかに効率的で、おそらく拡張性も高くなります。
ペリアタブレアッタ

1
条件変数も考慮してください。彼らはこのような多くのデータ構造を非常に簡単にすることができます。
コートアンモン-モニカの復活

2

他の人が言ったようis_lockedに、ミューテックスでメリットがあるユースケースはありません。そのため、関数は存在しません。

あなたが問題を抱えている場合は、それはの一つであるワーカースレッドが、そうでない場合は何をすべきか、基本的です、信じられないほど一般的であり、スレッドの最も一般的な実装を。

10個の箱がある棚があります。これらのボックスで作業する4人の労働者がいます。4人の労働者が異なるボックスで作業することをどのように確認しますか?最初の作業者は、作業を開始する前に棚から箱を取り出します。2人目の作業者には、棚に9つのボックスがあります。

ボックスをロックするミューテックスはないため、ボックス上の仮想ミューテックスの状態を確認する必要はなく、ミューテックスをブール値として乱用するのは間違っています。ミューテックスはシェルフをロックします。


1

上記の5gon12ederの回答で与えられた2つの理由に加えて、それは必要でも望ましくもないことを付け加えたいと思います。

既にミューテックスを保持している場合は、それを保持していることを知っておく必要があります。尋ねる必要はありません。メモリまたは他のリソースのブロックを所有するのと同じように、それを所有しているかどうか、およびリソースの解放/削除がいつ適切かを正確に知る必要があります。
そうでない場合は、プログラムの設計が不適切であり、問​​題に向かっています。

ミューテックスで保護されている共有リソースにアクセスする必要があり、まだミューテックスを保持していない場合は、ミューテックスを取得する必要があります。他のオプションはありません。それ以外の場合、プログラムロジックは正しくありません。
あなたはどちらの場合には、許容可能または許容できない閉塞見つけるかもしれないlock()またはtry_lock()あなたが望む行動を与えるだろう。確実に、そして間違いなく知る必要があるのは、ミューテックスを正常に取得したかどうかです(の戻り値がわかりtry_lockます)。他の誰かがそれを保持しているかどうか、またはあなたが偽の失敗に陥ったかどうかは重要ではありません。

それ以外の場合、率直に言って、それはあなたのビジネスのどれでもありません。知っている必要はありませんし、知っているべきではありませんし、仮定するべきでもありません(他の質問で言及されている適時性と同期の問題のため)。


1
現在ロックに使用できるリソースでランキング操作を実行したい場合はどうすればよいですか?
quant

しかし、それは現実的なことですか?私はそれをかなり珍しいと思います。どちらかのリソースが既に何らかの固有のランキングを持っていると言ったら、最初にもっと重要なものを実行する(ロックを取得する)必要があります。例:レンダリングの前に物理シミュレーションを更新する必要があります。または、ランキングは多かれ少なかれ意図的である場合try_lock、最初のリソースも同様に使用できます。そのリソースが失敗した場合は、2番目のリソースを試してください。例:データベースサーバーへの3つの永続的なプールされた接続。1つを使用してコマンドを送信する必要があります。
デイモン

4
@quant-「現在ロックに使用できるリソースのランキング操作」-一般に、この種のことを行うことは、把握するのに苦労する方法でデッドロックするコードを書くための本当に簡単で迅速な方法です。ロックの取得と解放を決定論的にすることは、ほとんどすべての場合において最良のポリシーです。変更される可能性のある基準に基づいてロックを検索することは、トラブルを求めています。
ペリアタブレアッタ

@PeriataBreatta私のプログラムは意図的に不確定です。この属性は一般的ではないことがわかりましたので、このようis_locked()な動作を容易にするような機能の省略を理解できます。
quant

@quantランキングとロックはまったく別の問題です。ロックを使用してキューを何らかの方法で並べ替えたり並べ替えたりする場合は、ロックしてロックし、並べ替えてからロックを解除します。必要なis_locked場合は、考えているよりもはるかに優れた問題の解決策が存在します。
ピーター

1

デフォルトのメモリ順序でatomic_flagを使用したい場合があります。データの競合はなく、mutexが複数のロック解除呼び出しで行うように例外をスローすることはありません(そして、制御不能にアボートします、私は追加するかもしれません...)。あるいは、アトミックがあります(例:atomic [bool]またはatomic [int](三角括弧で、[]ではありません))。loadやcompare_exchange_strongなどの便利な関数があります。


1

このためのユースケースを追加したいと思います。これにより、内部関数は、呼び出し側が実際にロックを保持していることを前提条件/アサーションとして保証できます。

いくつかのそのような内部関数、およびおそらくそれらを呼び出す多くのパブリック関数を持つクラスの場合、内部関数を呼び出す別のパブリック関数を追加する人が実際にロックを取得することを保証できます。

class SynchronizedClass
{

public:

void publicFunc()
{
  std::lock_guard<std::mutex>(_mutex);

  internalFuncA();
}

// A lot of code

void newPublicFunc()
{
  internalFuncA(); // whops, forgot to acquire the lock
}


private:

void internalFuncA()
{
  assert(_mutex.is_locked_by_this_thread());

  doStuffWithLockedResource();
}

};
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.