0

私はEmbarcaderoのクリティカルセクションの実装TCriticalSection、を使用していますが、そのドキュメントは私の質問に答えていません。私たちはグローバルクリティカルセクションオブジェクトがある場合スレッドがクリティカルセクションでAcquire()を呼び出すと、異なるスレッドがRelease()を呼び出すと、そのロックは解放されますか?

は:

namespace 
{ 
    //delphi style class must be constructed on the heap 
    TCriticalSection* criticalSection = new TCriticalSection(); 
} 

//somewhere in thread 1... 
criticalSection->Acquire(); 

//somewhere in thread2... 
criticalSection->Release(); 

をスレッド2でリリース呼び出しがクリティカルセクションを開くのだろうか?

破壊中にロックを解除するRAIIクラスがあるため、コードが例外をスローするとデッドロックには入らないため、私は質問します。ただし、クリティカルセクションはメソッドの一部に過ぎません。

//... 
CRITICAL_SECTION_LOCK lock(criticalSection); 
OneAtATimePlease(); 
lock.Release(); 
//... 

その後、我々は、コードでそのメソッドへのすべての単一の呼び出しのためにロックを処理することがありますので、だから私は)OneAtATimePlease(内部のクリティカルセクションを配置する必要はありません。

答えて

1

TCriticalSectionAcquire()は、単にEnterCriticalSection()を呼び出し、Release()は、単にLeaveCriticalSection()を呼び出しWin32のCritical Section object、のラッパーです。

LeaveCriticalSection()ドキュメントの状態:

スレッドがクリティカルセクションオブジェクトの所有権を取得するEnterCriticalSection又はTryEnterCriticalSection関数を使用します。所有権を解放するには、スレッドはクリティカルセクションに入るたびにLeaveCriticalSectionを呼び出す必要があります。それは指定されたクリティカルセクションオブジェクトの所有権を持っていない場合、スレッドがLeaveCriticalSectionを呼び出す場合

、エラーはそれが無期限に待機しEnterCriticalSectionを使用して別のスレッドを引き起こす可能性が発生します。

したがって、現在ロックを所有していないスレッドでクリティカルセクションのロックを解除しないでください。

あなたはそれが属するOneAtATimePlease()、内部のあなたのlock変数を移動することは完全にOK(そして好ましい)である:

void OneAtATimePlease() 
{ 
    CRITICAL_SECTION_LOCK lock(criticalSection); 
    ... 
} 

は、複数のスレッドがでOneAtATimePlease()を呼び出す場合、元のコードを使用して、何が起こるかを考えてみて同時にしかし、スレッドがクリティカルセクションロックしない:

スレッド1

CRITICAL_SECTION_LOCK lock(criticalSection); 
OneAtATimePlease(); 
lock.Release(); 

をスレッド2

CRITICAL_SECTION_LOCK lock(criticalSection); 
OneAtATimePlease(); 
lock.Release(); 

スレッド3

// NO LOCK!!! 
OneAtATimePlease(); 

スレッド1または2がその中にすでにある一方で、スレッド3はOneAtATimePlease()を実行することができます!それはクリティカルセクションを使用する目的全体を打ち負かします。 OneAtATimePlease()の内部でロックを移動すると、複数のスレッドが互いに同期しなくなることはありません(ロックが所有されていないときに重大なセクションが誤ってロックされていない限り、RAIIラッパーはそれを防ぎます) 。これもdocumentationごとに、再帰的にかつ安全に働くだろう

スレッドがクリティカルセクションを所有している場合は、その実行をブロックすることなくEnterCriticalSectionまたはTryEnterCriticalSectionへの追加の呼び出しを行うことができます。これにより、既に所有しているクリティカルセクションを待つ間にスレッドがデッドロックすることを防ぎます。所有権を解放するには、スレッドはクリティカルセクションに入るたびにLeaveCriticalSectionを1回呼び出す必要があります。

void OneAtATimePlease() 
{ 
    CRITICAL_SECTION_LOCK lock(criticalSection); 
    ... 
    if (some condition) 
     OneAtATimePlease(); 
    .... 
} 
+0

明確で詳細な回答をありがとう! –

2

「クリティカルセクション」は、実際にはMS Windowsの概念です。スレッドがクリティカルセクションのロック解除に失敗し、ロックされていないクリティカルセクションのロックを別のスレッドがロックしようとするとどうなるかは未定義です。

のロックは、クリティカルセクションが共有されているにもかかわらずスレッドローカルであるため、各スレッドは独自のロックを解除できないため、(別のスレッドがクリティカルセクションをロック解除する)ことはできません。

+0

私は、二重解放のイベントを処理するためにRAIIクラスにいくつかのロジックを追加しましたが、CRITICAL_SECTION_LOCKのリリース()メソッドは、単にcriticalSection->リリースを()やってしまいます。したがって、ロックオブジェクトはスレッドローカルですが、操作はグローバルなTCriticalSectionにあります。そのため、異なるスレッドから解放することは定義されません。 –

+0

@syco_link:デバッグモードで 'assert'を追加して、問題のコードを捕まえることができます。そして、はい、クリティカルセクションのロックを解除しようとすると、他のスレッドのランダムなロックではなく、現在のスレッドが保持するロックのみを解放することができます。 – MSalters

関連する問題