lock(syncObject)はいつ例外をスローできますか?
-
06-07-2019 - |
質問
.NETでcomコンポーネントを作成しましたが、任意のメソッド(comコンポーネントと通信するアンマネージコードによって呼び出される)でオブジェクトをロックしようとすると、例外が発生します。
現時点では例外の正確なテキストはありませんが、あまり役に立ちませんでした。
だから私の質問は、どのような状況でlock(syncObject)が例外をスローするのでしょうか? いくつかの事実があります:
- syncObjectがnullではありません
- syncObjectはまだロックされていません
STA(シングルスレッドアパートメント)またはMTA(マルチスレッドアパートメント)で実行されている呼び出し先と関係がありますか?
解決
このページから:
ロックを取得するたびに例外がスローされる場合があります。準備してください。
ロックの取得でCLRモニターなどの競合が発生した場合、ほとんどのロックは遅延的にイベントを割り当てます。この割り当ては、リソース不足の状態では失敗し、ロックへの入り口から発生するOOMを引き起こす可能性があります。 (一般的な非ブロッキングスピンロックはOOMで失敗することはありません。これにより、CER内部などのリソースに制約のあるシナリオで使用できるようになります。)同様に、SQL Serverなどのホストは、 Enterステートメントから発生する例外を生成し、System.Runtime.InteropServices.COMExceptionとして現れます。
このような例外に応じてできることは多くないことがよくあります。ただし、障害にロバストに対処する必要がある信頼性とセキュリティに敏感なコードでは、このケースを考慮する必要があります。ホストのデッドロックにインテリジェントに応答できる場合が望ましいと思いますが、ほとんどのライブラリコードはスタックの安全なポイントまでインテリジェントに巻き戻せないため、操作をバックオフして再試行できます。典型的なスタックでは、単にクロスライブラリの混在が多すぎます。これが、TryEnterを使用したタイムアウトベースのモニター取得がデッドロック防止のために一般的に悪い考えである理由です。
このように、リソースが限られている状況では、lock(o)が内部的に使用するMonitorのEnterメソッドから例外がスローされる可能性があるようです。
あなたの解決策はスピンウェイトのようなものですか?
{
uint iters = 0;
while (!cond) {
if ((++iters % 50) == 0) {
// Every so often we sleep with a 1ms timeout (see #30 for justification).
Thread.Sleep(1);
} else if (Environment.ProcessorCount == 1) {
// On a single-CPU machine we yield the thread.
Thread.Sleep(0);
} else {
// Issue YIELD instructions to let the other hardware thread move.
Thread.SpinWait(25);
}
}
}
condが存在する可能性のある場所
private volatile int cond = 0
たとえばInterlocked.CompareExchangeで、たとえば、 Thread.Current.ManagedThreadIDまたはゼロ以外の何か?