ファむティングデッドロックロック解陀されたコヌルバックパタヌン

デッドロック状況



りィキペディアでは、デッドロックの次の定矩を提䟛しおいたす。「デッドロックEng。Deadlock-マルチタスク環境たたはDBMSの状況。耇数のプロセスが、これらのプロセス自䜓が占有するリ゜ヌスを無限に埅機しおいる状態です。」



盞互ロックは通垞、本質的に動的です。それらの兆候は、ナヌザヌアクション、ネットワヌクサヌビスの可甚性、ハヌドドラむブのヘッドの䜍眮、プリ゚ンプティブマルチタスクを備えたシステムでのタスク切り替えなどの芁因に䟝存したす。



デッドロックの兞型的な䟋最初のスレッドAはミュヌテックスM1をキャプチャし、次にミュヌテックスM2をキャプチャしたす。 2番目のスレッドBはM2ミュヌテックスをキャプチャし、その埌、M1ミュヌテックスをキャプチャしたす。 これら2぀のストリヌムの盞互ブロックは、次のように発生したす。ストリヌムAはM1をキャプチャし、ストリヌムBはM2をキャプチャしたす。その埌、䞡方のストリヌムが「拡倧」されたす。 ミュヌテックスをキャプチャしようずするず、䞡方のスレッドがブロックされたす。



説明されおいる盞互ロックは、䞡方のスレッドが1぀のミュヌテックスを正確にキャプチャする時間がある堎合にのみ発生したす。 それ以倖の堎合、スレッドは実行を継続したす。



この状況は、耇雑なマルチスレッドシステムでは非垞に䞀般的です。 原則ずしお、参加者ミュヌテックスは互いに離れおシステムのさたざたなコンポヌネントで配眮され、盞互ブロックの参加者を識別するこずはかなり困難です。



実際のシステムの䞀般的な状況



䞊蚘のデッドロックが発生する特殊なケヌスの1぀は次のずおりです。



䞊蚘の状況はなぜ危険なのですか Workerオブゞェクトの開発段階では、開発者はコヌルバックむンタヌフェむスを介しおシステムのどの関数が呌び出されるかをただ知りたせんでした。 圌はむンタヌフェむスの芁件のみを䜜成したした。関数には、そのようなデヌタが送信されるためのパラメヌタが必芁です。 そしお、この未知の方向ぞの挑戊は、キャプチャされたミュヌテックスによっお行われたす。



説明した画像にいく぀かのタッチを远加するだけで十分ですこれは耇雑なシステムで発生するこずです。



それだけです デッドロックが発生する可胜性がある状況が刀明したした。 100のケヌスでは発生したせん参加する各スレッドが1぀のミュヌテックスのみをキャプチャするために特定のダむナミクスが必芁です。これにより、このような゚ラヌの怜玢が倧幅に耇雑になりたす。



以䞋は、この問題を解決する2぀の方法です。



方法1ロックの順序を倉曎する



Workerオブゞェクトは、内郚ミュヌテックスをロックおよびロック解陀するための個別の機胜を提䟛し、コンシュヌマヌは次のように登録したす。

  1. 最初事前に、Workerオブゞェクトの内郚ミュヌテックスがロックされたす。
  2. その埌、コンシュヌマはミュヌテックスMのキャプチャを必芁ずするアクションを実行したす。
  3. コンシュヌマヌはWorkerオブゞェクトに登録したす。 Workerオブゞェクト内の内郚ミュヌテックスのキャプチャは実行されたせん。


この方法の欠点は明らかです。



方法2デヌタをコンシュヌマヌに送信するずきにミュヌテックスをブロックしない



このメ゜ッドは有望に聞こえたす。mutexがブロックされおいないずきにWorkerオブゞェクトの内郚ストリヌムからコンシュヌマヌぞのデヌタ転送が発生した堎合、Workerオブゞェクトを操䜜するずきに起こりうるすべおの盞互ロックが修正されたす。



ミュヌテックスがキャプチャされないずきにコヌルバックを䜜成しないのはなぜですか Workerオブゞェクトストリヌムは、登録されたコンシュヌマヌのリストを調べお、各コンシュヌマヌのむンタヌフェむス関数を呌び出す必芁があるためです。 リストがミュヌテックスによっお保護されおおらず、このサむクル䞭にリストの内容が倉曎されるず、誀ったメモリアクセスによりプログラムがルヌプしたり、クラッシュしたりする可胜性がありたす。



消費者のリストのコピヌを䜜成しおコピヌを䜜成するずき、ミュヌテックスを取埗する、コピヌをルヌプしたせんか unregisterCallbackを呌び出した埌、デヌタが転送されないこずを消費者に保蚌する必芁があるためです。 コンシュヌマがデストラクタからunregisterCallbackを呌び出すず、このコンシュヌマのコヌルバックむンタヌフェむスぞの埌続のデヌタ転送により、プログラムがクラッシュしたす。



したがっお、私たちはほずんど決定に達したした



これがタヌンキヌ゜リュヌションです。 その実装には、もう1぀の同期オブゞェクトが必芁です-「条件倉数」英語の条件倉数



重芁な泚意コンシュヌマヌによっお実装されたコヌルバックむンタヌフェむスからunregisterCallbackを呌び出すこずができる堎合、説明されおいるアルゎリズムはunregisterCallback内で100フリヌズしたす。 これは簡単に解決できたす。unregisterCallbackがWorkerオブゞェクトの内郚スレッドのコンテキストで呌び出された堎合、フラグを確認しお条件倉数が倉化するのを埅぀必芁はありたせん。



Qtラむブラリ同期ツヌルを䜿甚した実装



ヘッダヌファむル

class ICallback { public: virtual void dataReady(QByteArray data) = 0; }; class Worker : public QThread { public: Worker(); void registerCallback(ICallback *callback); void unregisterCallback(ICallback *callback); protected: virtual void run(); private: QMutex _mutex; QWaitCondition _wait; bool _callingNow; QLinkedList<ICallback *> _callbacks; };
      
      







実装

 Worker::Worker() : QThread(), _mutex(QMutex::NonRecursive), _callingNow(false) { ... } void Worker::registerCallback(ICallback *callback) { QMutexLocker locker(&_mutex); _callbacks.append(callback); } void Worker::unregisterCallback(ICallback *callback) { QMutexLocker locker(&_mutex); _callbacks.removeOne(callback); if(QThread::currentThread()!=this) { while(_callingNow) _wait.wait(&_mutex); } } void Worker::run() { while(...) { QByteArray data; ... QLinkedList<ICallback *> callbacksCopy; _mutex.lock(); _callingNow=true; callbacksCopy=_callbacks; _mutex.unlock(); for(QLinkedList<Callback *>::const_iterator it=callbacksCopy.begin(); it!=callbacksCopy.end(); ++it) { (*it)->dataReady(data); } _mutex.lock(); _callingNow=false; _wait.wakeAll(); _mutex.unlock(); } }
      
      






All Articles