ミューテックスの使用法は非常に単純に思えます。コードには、任意の時点で 1 つのスレッドのみがアクセスできるクリティカル セクションがあります。これはミューテックス m によって保証されます。 m.lock() と m.unlock() の呼び出しは、この排他性を保証します。しかし、悪魔は細部に宿ります。
デッドロック
デッドロックのさまざまな名前は恐ろしいものです。それらを致命的な抱擁(死の抱擁:-)と呼ぶ人もいますか?または死のキス。でも待ってください、デッドロックとは何ですか?
- デッドロック
- デッドロックとは、少なくとも 2 つのスレッドがブロックされている状態です。これは、各スレッドが、自分のリソースを解放する前に、他のスレッドが使用するリソースの解放を待っているためです。
-
デッドロックの結果は、完全な停止です。スレッドと通常はプログラム全体が永久にブロックされます .デッドロックが発生しやすい。興味がありますか?
例外と不明なコード
std::mutex m; m.lock(); sharedVariable= getVar(); m.unlock();
関数 getVar() 内の不明なコードが例外をスローした場合、 m.unlock() は呼び出されません。ミューテックス m を求める試みはすべて失敗し、プログラムはブロックされます。永遠に。しかし、そのコードの問題はそれだけではありません。 m.lock() がアクティブなときに、いくつかの (私たちには知られていない) 関数 get.Var() を呼び出します。関数 getVar() が同じロックを取得しようとするとどうなりますか?もちろん、あなたはそれを知っています。デッドロック。
より視覚的な例が必要ですか?
異なる順序でミューテックスをロックする
スレッド 1 とスレッド 2 は、作業を完了するために 2 つのリソースにアクセスする必要があります。残念ながら、彼らは異なる順序で 2 つのミューテックスによって保護されているリソースを要求します。この場合、スレッドの実行は、スレッド 1 がミューテックス 1 を取得し、次にスレッド 2 がミューテックス 2 を取得するようにインターリーブし、停止します。各スレッドは、他のスレッドのミューテックスを取得したいと考えています。このために、スレッドはリソースの解放を待つ必要があります。
コードで絵を表現するのは簡単です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | // deadlock.cpp #include <iostream> #include <chrono> #include <mutex> #include <thread> struct CriticalData{ std::mutex mut; }; void deadLock(CriticalData& a, CriticalData& b){ a.mut.lock(); std::cout << "get the first mutex" << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(1)); b.mut.lock(); std::cout << "get the second mutex" << std::endl; // do something with a and b a.mut.unlock(); b.mut.unlock(); } int main(){ CriticalData c1; CriticalData c2; std::thread t1([&]{deadLock(c1,c2);}); std::thread t2([&]{deadLock(c2,c1);}); t1.join(); t2.join(); } |
スレッド t1 とスレッド t2 がデッドロック関数を呼び出します (12 ~ 20 行目)。デッドロックを処理するには、両方の関数で CriticalData c1 と c2 が必要です (27 行目と 28 行目)。オブジェクト c1 と c2 は共有アクセスから保護する必要があるため、ミューテックスを持っています (この例のコードを短くシンプルにするために、CriticalData にはミューテックス以外のメソッドやメンバーはありません)
16 行目で約 1 ミリ秒しかスリープしないため、デッドロックが発生します。
現在唯一の選択肢は、CTRL+C を押してプロセスを強制終了することです。
次は?
正直なところ、この例はマルチスレッド プログラムを作成する自信を高めるものではありません。さらに、新しいミューテックスごとに複雑さが 2 の累乗に増加します。ロックはミューテックスを安全な方法でカプセル化するため、この問題の解決策はロックです。どのように?こちらをご覧ください。 (校正者アレクセイ エリマノフ )