4

ここにあなたたちのためのパズルがあります。一部のスレッドがロックやセマフォなどの管理対象リソースを操作するマルチスレッド プログラムがあります。ロック解放プリミティブの一部は、ロックの取得が行われた同じスレッドからのみ実行できます。

だからここに私のパズルがあります: 私はこれらの種類の操作をラップします: try { lock-acquire... do something } finally { lock-release } しかし、スレッドが終了すると、finally 句が .NET ガベージ コレクション スレッドによって実行され、私のスレッドではありません。(特定のケースには、実際には「using」ステートメントで割り当てられたオブジェクトの Dispose が含まれます。詳細については、以下を参照してください)

これはデモが少し難しいです。私は Isis2 でいつもそれを目にします。私は、取得ブロックとファイナライズ ブロックのスレッド ID をチェックすることで、これが起こっていることを理解しました。しかし、3 行のデモはありません。申し訳ありません。私はそれが助けを提供しやすくすることを知っています.

そのスレッドに関連付けられているすべての保留中のファイナライズ ブロックが実行されるまで、スレッドの終了を遅らせる方法はありますか?

---- マークの詳細を追加 ----

私が実際に行っているのは、スレッドの優先度を備えたさまざまなロックの抽象化 (境界のあるバッファー、バリア、通常のロックなど) を持ち、デッドロック、優先度の逆転、非常に遅いロック付与を自己検出するように設計された、かなり精巧な自己計装ロック パッケージです。その他の問題。私のコードは十分に複雑で十分にスレッド化されているため、デバッグするにはこれが必要でした。

私の基本的な構成スタイルの例は次のとおりです。

LockObject  myLock = new LockObject("AnIsis2Lock");

...

using(new LockAndElevate(myLock)) { stuff }

LockObject は自己監視ロックです... LockAndElevate は、ロックしたことを記録し (この場合)、ロックを解除したときに破棄を追跡します。したがって、例外がスローされたとしても、ブロックが完了すると新しいオブジェクトを破棄する必要があるという事実を利用しています。

私が見ているのは、かなり頻繁にスレッドが終了し、まだ使用中の破棄イベントが実際に発生していないことです。それらは後で別のスレッドで実行されます。これは、スレッドの 1 つが終了したときにのみ発生します。通常の実行では、すべてが魔法のように機能します。

したがって、変換を使用して試してから...最後に、私の質問はfinally句の観点から投稿されました。

4

1 に答える 1

2

現時点では、Isis2 の動作をデバッグした経験に基づいた、私自身の質問に対する最良の回答を以下に示します。

スレッドが終了しない場合、"using(x = new something()) { }" (これは "try { x= new something(); ...} finally { x.dispose }" にマップされます) は、あなたとまったく同じように機能します。期待される: コード ブロックが終了すると、dispose が発生します。

しかし、例外は制御フローを混乱させます。したがって、スレッドが IsisException をスローした場合、または「何か」の何かがスローした場合、制御はその例外のキャッチに渡されます。これは私が扱っているケースであり、私の catch ハンドラーはスタックの上位にあります。C#/.NET は選択に直面しています。最初に IsisException をキャッチするか、それとも最初に破棄するか? この場合、システムが最初に IsisException を体系的に実行していると確信しています。その結果、割り当てられたオブジェクト「x」のファイナライザーはまだ実行されていませんが、実行可能であり、すぐに呼び出す必要があります。

[NB: 興味のある方のために説明すると、Using ステートメントは Dispose を呼び出すことで終了しますが、ドキュメントによると、推奨される動作はファイナライザー ~something() { this.Dispose; を使用することです。考えられるすべてのコード パスをカバーするだけです。その後、Dispose が複数回呼び出される可能性があるため、フラグを保持し、同時実行に対してロックし、Dispose の最初の呼び出しでのみ管理対象リソースを破棄する必要があります。]

ここで重要な問題は、スレッドを終了させるキャッチされた例外の場合、スレッドが終了する前にファイナライザーが明らかに実行されない可能性があることです。そうでない場合、C# は GC ファイナライザー スレッドで dispose を呼び出してオブジェクトを破棄します。その結果、私のコードのように x.Dispose() が何かをロック解除すると、エラーが発生する可能性があります。ロックの取得/解放は、.NET の同じスレッドで発生する必要があります。これは、あなたにとっても私にとっても潜在的なバグの原因です! この場合、例外ハンドラで GC.WaitForFinalizers を呼び出すと役立つようです。これが悪いことが起こらないという真の保証であるかどうかは、私にはあまり明確ではありません.

私自身のコードのさらに重大な誤りは、いくつかのスレッドで ThreadAbortException を誤ってキャッチしていたことです。これをしないでください。.NET に深刻な問題を引き起こすことがわかりました。Thread.Abort を使用しないでください。

この理解に基づいて、Isis2 を修正したところ、うまく動作するようになりました。スレッドが終了すると、ファイナライザーが正しく実行されているように見え、スレッドが終了する前に破棄が行われるように見えます (したがって、スレッドの ID を再利用できるようになる前に、混乱を引き起こしました)。すべてが世界にとって良いことです。スレッド、優先度、および自己管理型ロック/バリア/バウンド バッファとセマフォを使用する場合は注意が必要です。ここにはドラゴンが潜んでいます!

于 2012-07-21T11:38:49.477 に答える