6

私は自分のプログラムでデッドロックの可能性を探していますが、次のことを疑っています。2 つのスレッドが同時に EnterCriticalSection を呼び出し、スレッド #1 が入った直後に DeleteCriticalSection を呼び出した場合、まだ EnterCriticalSection 呼び出しにあるスレッド #2 はどうなりますか?

ありがとう。

4

1 に答える 1

13

2 つのスレッドが同時に EnterCriticalSection を呼び出し、スレッド #1 が入った直後に DeleteCriticalSection を呼び出した場合、まだ EnterCriticalSection 呼び出しにあるスレッド #2 はどうなりますか?

2 つのスレッドが同時にクリティカル セクションに入ることができません。それは、クリティカル セクションの目的を無効にします。スレッド #1 が最初にクリティカル セクションに入るか、スレッド #2 が最初にクリティカル セクションに入ります。ここでは、2 つのインターリーブが可能です。

インターリーブがこれであるとしましょう:

    スレッド 1 スレッド 2
    -------- --------
       | | | |
       | | | |
    EnterCS() |
    ロックが取られました |
       | | | |
       | | EnterCS()
       | | ブロックされた
       | | | |
       | | | |
    削除CS() |
       | | | |
       | | ???
       | |
      ...

この場合、 MSDN によると、スレッド #2 の状態は未定義です。

DeleteCriticalSection 関数

備考

クリティカル セクション オブジェクトを削除すると、そのオブジェクトが使用していたすべてのシステム リソースが解放されます。

クリティカル セクション オブジェクトが削除された後は、InitializeCriticalSection と InitializeCriticalSectionAndSpinCount 以外のクリティカル セクション (EnterCriticalSection、TryEnterCriticalSection、LeaveCriticalSection など) で動作する関数でオブジェクトを参照しないでください。そうしようとすると、メモリの破損やその他の予期しないエラーが発生する可能性があります。

クリティカル セクションが所有されている間に削除された場合、削除されたクリティカル セクションの所有権を待機しているスレッドの状態は未定義です。

したがって、2 つのスレッドが上記のインターリーブに遭遇するほど不運だった場合、オペレーティング システムは、プログラムが期待どおりに動作し続けることを保証しません。これには、たとえばスレッド #2 のデッドロックが含まれる場合があります。

しかし、インターリーブが次の場合:

    スレッド 1 スレッド 2
    -------- --------
       | | | |
       | | | |
       | | EnterCS()
       | | ロックが取られました
       | | | |
    EnterCS() |
    ブロックされました |
       | | | |
       | | | |
       | | ExitCS()
       | | ロック解除
       | | | |
    ブロック解除 |
    ロックテイクン |
       | | | |
    削除CS() |
       | | | |
       | | | |
      ... ... ...

次に、明らかに、スレッド #1 がブロックされているため、スレッド #2 がクリティカル セクションを離れるまで、クリティカル セクションを削除できません。次に、他のスレッドがクリティカル セクションに入らないと仮定すると、スレッド #1 は問題なく削除できます。

あなたが提案するシナリオは、本質的に競合状態です。スレッドのタイミングによっては、問題なく動作するか、予期しない問題が発生する可能性があります。この場合、関連するすべてのスレッドがクリティカル セクションを解放した後にクリティカル セクションが破棄されるように、コードを再構築する必要があります。

この 2 スレッドのシナリオで、これを修正する 1 つの方法は、クリティカル セクションを削除する前に、スレッド #1 をクリティカル セクションから離れさせ、他のすべてのスレッドが最初に終了するのを待つことです。たとえば、次のようなものです。

// Pseudocode for exposition
void Thread1()
{
    EnterCS();
    // Do stuff
    ExitCS();
    WaitForThread2();
    DeleteCS();
}

void Thread2()
{
    EnterCS();
    // Do stuff
    ExitCS();
}

ここで、考えられる 2 つのインターリーブは次のようになります。

    スレッド #2 が最初にロックを取得します: . スレッド #1 が最初にロックを取得します。
                                    .
    スレッド 1 スレッド 2 。スレッド 1 スレッド 2
    -------- -------- . -------- --------
       | | | | . | | | |
       | | CS() を入力します。EnterCS() |
       | | ロックがかかりました。ロックが取られました |
       | | | | . | | | |
    EnterCS() | . // 処理を行う EnterCS()          
    ブロックされた // 何かを行う . | | ブロックされた
       | | | | . | | | |
       | | | | . ExitCS() |
       | | ExitCS() . ロック解除 |
       | | ロックが解除されました。| | | |
       | | | | . | | ブロック解除
    ブロック解除 | . | | ロックが取られました
    ロックが取られました | . | | | |
       | | | | . | | // 何かをする
   // 何かをする | . | | | |
       | | | | . | | ExitCs()
    ExitCS() | . | | ロック解除
    ロック解除 | . | | | |
       | | | | . | | | |
       | | | | . | | | |
    WaitForThread2() --+ . WaitForThread2() --+
       | | . | |
    CS() を削除します。削除CS()
       | | . | |
       | | . | |
      終わり 。終わり

の正確な実装WaitForThread2()は、プログラムの性質によって異なりますが、確実にWaitForSingleObject()またはその親戚のいずれかが関与します。

于 2012-10-08T01:55:05.977 に答える