4

私のプロジェクトには、スレッド自体、他のスレッド、またはVCL(メインアプリ)によって変更される可能性のあるスレッドがあります。したがって、私はすべてのデータアクセスにTCriticalSection.Acquire/Releaseを使用しています。

通常の状況では、次のコードは期待どおりに機能します。Acquireに入り、DoCallbackと同期してから、ロックを解除します。ただし、他のコンテキストのいずれかがすでにロックされているときにロックを取得した場合、以下のコードの実行はSynchronizeで停止します。今回は、DoCallbackメソッドに入りません。

Synchronizeメソッドをスキップして(SynchronizeのコードがVCLを呼び出す場合でも)、CriticalSection自体に依存する必要がありますか?この動作の理由は何ですか?

メインスレッドのコード:

  fData:= nil;
  try
    fSingleRequest.Acquire;      
    if fItem <> nil then
      begin
        fData:= fItem.Request;
        SubmitRequest();
        fCallbackData:= fItem.fExtraData;
        fCallback:= fItem.fCallback;
        Synchronize(DoCallback); // <-- this line is called
      end;
  finally
    fSingleRequest.Release;      // <-- this isn't under the specific situation
  end;
4

1 に答える 1

9

いわゆる「メイン」スレッドがクリティカルセクションを取得し、次にVCLスレッド(通常は「メイン」スレッドと呼ばれるもの)がそれを取得しようとすると、VCLスレッドはクリティカルセクションが解放されるまでブロックします。 。次に、「メイン」スレッドがを呼び出しますSynchronize。これは、VCLスレッドのコンテキストで特定の関数を実行します。VCLスレッドはクリティカルセクションで待機するようにブロックされているため、メッセージを処理していないため、呼び出す同期メソッドがあることに気付くことはありません。したがって、デッドロック。

スレッド内呼び出し間でロックを保持しないでください。を呼び出す前に「メイン」スレッドのロックを解除し、Synchronizeそれでも必要な場合は、後で再度取得します。同期方式で使用されているデータを同時アクセスから継続的に保護する必要がある場合は、データを別の非共有オブジェクトにコピーする方法を見つける必要があると思います。同期されたメソッドに、共有オブジェクトの代わりにそのオブジェクトを使用させ、後でそれを破棄します。

あなたのコメントは、なしでコールバック関数を呼び出すことができSynchronize、すべてが機能しているように見えることを示しています。その場合、同期されたメソッドは以前と同じスレッドコンテキストで呼び出されていません。VCLスレッドではなく、「メイン」スレッドによって直接呼び出されています。これにより、ロックの競合が明らかに緩和されますが、それが本当に安全かどうかは、コールバック関数の機能によって異なります。

于 2011-11-14T17:01:03.863 に答える