概要:
それは私には思われる:
- 論理状態を表すフィールドを単一の不変の消費可能なオブジェクトにラップする
- への呼び出しでオブジェクトの正式な参照を更新する
Interlocked.CompareExchange<T> - 更新の失敗を適切に処理する
は、「ロック」構造を不必要にするだけでなく、並行性に関する特定の現実をかわし、結果として多くの新しい問題を引き起こす真に誤解を招くような構造にする一種の並行性を提供します。
問題のディスカッション:
まず、ロックを使用する際の主な問題を考えてみましょう。
- ロックはパフォーマンス ヒットを引き起こすため、読み取りと書き込みには同時に使用する必要があります。
- ロックはスレッドの実行をブロックし、並行性を妨げ、デッドロックのリスクを高めます。
「ロック」に触発されたばかげた動作を考えてみましょう。リソースの論理セットを同時に更新する必要が生じた場合、リソースのセットを「ロック」します。これは、関連付けが緩い専用のロック オブジェクトを介して行います。これは、そうでなければ何の役にも立ちません (赤旗 #1)。
次に、「ロック」パターンを使用して、一連のデータ フィールドで論理的に一貫した状態変更が発生するコードの領域をマークオフしますが、フィールドを同じオブジェクト内の無関係なフィールドと混合することで自分自身を撃ちます。それらをすべて可変のままにし、これらのさまざまなフィールドを読み取るときにロックを使用する必要があるコーナー (赤旗 #2) に追い込むことで、一貫性のない状態でそれらをキャッチしないようにします。
明らかに、その設計には深刻な問題があります。ロックオブジェクトの慎重な管理 (ロック順序、ネストされたロック、スレッド間の調整、何かをするのを待っている別のスレッドによって使用中のリソースのブロック/待機など) が必要なため、やや不安定です。コンテキスト。また、デッドロックを回避するのは「難しい」と言う人もいますが、実際には非常に簡単です。あなたのためにレースを走るように頼む予定の人の靴を盗まないでください!
解決:
「ロック」の使用を完全に停止します。 一貫性のある状態またはスキーマを表す、破損しない/不変のオブジェクトにフィールドを適切にロールバックします。おそらく、表示名と内部識別子を相互に変換するための辞書のペアであるか、値と次のオブジェクトへのリンクを含むキューのヘッド ノードである可能性があります。それが何であれ、それを独自のオブジェクトにラップし、一貫性のために封印します。
書き込みまたは更新の失敗を可能性として認識し、それが発生したときにそれを検出し、無期限にブロックするのではなく、すぐに (または後で) 再試行するか、別のことを行うかを状況に応じて決定します。
ブロッキングは、実行する必要があると思われるタスクをキューに入れるための簡単な方法のように思えますが、すべてのスレッドが専用でセルフサービス型であるため、システム全体を危険にさらすリスクを冒してそのようなことを行う余裕があるわけではありません。「ロック」を使用して物事をシリアル化するのが面倒であるだけでなく、書き込みが失敗してはならないふりをしようとすることの副作用として、スレッドをブロック/フリーズするため、スレッドが応答しなくなり、役に立たなくなり、他のすべての責任が放棄されます。自分の責任を果たすために他人を助けることが必要な場合があるという事実を知らずに、自分が以前にやろうとしていたことを達成するのを頑固に待ちます。
独立した自発的なアクションが同時に発生している場合、競合状態は正常ですが、制御されていないイーサネットの衝突とは異なり、プログラマーとして、「システム」(つまり、決定論的なデジタル ハードウェア) とその入力を完全に制御できます。 0 か 1 か?) と出力、およびシステムの状態を格納するメモリであるため、ライブロックは問題にならないはずです。さらに、多数のプロセッサが同時に動作している可能性があるという事実を解決するメモリ バリアを使用したアトミック操作があります。
要約する:
- 現在の状態オブジェクトを取得し、そのデータを消費して、新しい状態を構築します。
- 他のアクティブなスレッドがまったく同じことを行っており、あなたを打ち負かす可能性があることを認識してください。ただし、すべてが「現在の」状態を表す信頼できる参照ポイントを観察します。
- Interlocked.CompareExchange を使用して、作業の基になった状態オブジェクトがまだ最新の状態であるかどうかを同時に確認し、それを新しい状態に置き換えます。それ以外の場合は失敗し (別のスレッドが最初に終了したため)、適切な修正アクションを実行します。
最も重要な部分は、失敗をどのように処理し、馬に戻るかです。これは、私たちがライブロックを避ける場所であり、考えすぎたり、十分なことをしたり、正しいことをしたりしません。ロックは、スタンピードに乗っていても馬から落ちることは決してないという幻想を作り出し、スレッドがそのようなファンタジーの土地で空想にふけっている間、システムの残りの部分はバラバラになり、クラッシュして燃えることができます.
では、CompareExchange と不変の論理状態オブジェクトを使用したロックフリーの実装では、「ロック」コンストラクトが実行できること (より安定した方法で) を達成できないことはありますか?
これはすべて、ロックを集中的に処理した後、私が自分で実現したことですが、別のスレッドで検索した後、ロックフリーのマルチスレッドプログラミングは何かを簡単にしますか? 、何百ものプロセッサを備えた高度に並列なシステムに直面するとき、高度に競合するロックを使用する余裕がない場合、ロックフリープログラミングが非常に重要になるだろうと誰かが述べています。