2013-04-08 14 views
20

ReaderWriterLockSlimクラスはスレッドIDを使用してロックの所有者を確認するので、すべてのメソッドが同じスレッドで実行されるという保証がない非同期メソッドでも安全に使用できます。非同期メソッドでReaderWriterLockSlimを使用することは安全ですか

たとえば、

System.Threading.ReaderWriterLockSlim readerwriterlock = new System.Threading.ReaderWriterLockSlim(); 
    private async Task Test() 
    { 
     readerwriterlock.EnterWriteLock(); 
     await Task.Yield(); //do work that could yield the task 
     readerwriterlock.ExitWriteLock(); //potentailly exit the lock on a different thread 
    } 

答えて

21

非同期メソッド

はいでReaderWriterLockSlimを使用することが安全であるとなし。これを非同期メソッドで使用することは安全ですが、awaitにまたがってロックを入力して終了する非同期メソッドでは安全ではない可能性があります。

この場合、いいえ、これは必ずしも安全ではありません。

ExitWriteLockは、EnterWriteLockと呼ばれる同じスレッドから呼び出される必要があります。それ以外の場合は、SynchronizationLockExceptionがスローされます。 documentationからこの例外がスローされます。

現在のスレッドが書き込みモードでロックに入っていません。 Windowsフォーム:これは同じスレッドに戻って(つまり、物事を移動する代わりに、現在のSynchronizationContextがあった場所の環境で常にだった非同期方式で使用された場合

だけの時間、これは安全だろうがありますWPFなど)、ネストされた非同期呼び出しでは使用されませんでした。呼び出しチェーンの上位の親がConfigureAwait(false)のタスクを設定しました(Taskが同期コンテキストを取得できなくなる)。その特定のシナリオでは、await呼び出しが呼び出しコンテキストにマーシャリングするため、スレッドが維持されることがわかります。

+0

私はこの質問を読んでおり、あなたとスティーブン・クレアリーはお互いに矛盾しています。よくわかりません。私は、ロックが問題ではない理由を理解しています。なぜなら、最初の場所でロックを取得した同じスレッドだからです。同じスレッドでも、ReaderWriterLockSlimは常に環境に関係なく非同期で問題になります。 –

+1

@ DonBoxまあ、問題は「いつも」問題ではありません。うまくいきますが、危険です。私は、同様にそれを提案する。同じ文脈で言及したように、あなたは依然としてデッドロックの原因となる再入可能な問題を得ることができます - あなたのルーチンが一度も呼び出されないことがわかったら、それはうまくいくはずです。しかし、それは大きな "if"です。 –

19

いいえ。あなたの例のように、スレッドアファインコーディネーションプリミティブは使用しないでください。

awaitの後に別のスレッドを使用して再開できる問題を正しく特定しました。 asyncメソッドが早期に戻る方法のために別の問題があります。呼び出し元はロックが保持されていることを認識していません。

ReaderWriterLockSlimデフォルトでは非再帰的なロックであるため、別のasyncメソッドが同じロックを取得しようとすると、デッドロックが発生します。ロックを再帰的に行ったとしても、問題は終了します。ロックを保持している間は、任意のエンドユーザーコードを呼び出さないでください。これは基本的にはawaitを使用しているときのコードです。

SemaphoreSlim typeは(そのWaitAsyncメソッド経由)async -awareで、スティーブンToubは私のAsyncEx libraryで利用可能なもseries of async coordination primitivesを持っています。

+0

できますか?第3段落 の具体的な例を挙げてください。「別の非同期メソッドが同じロックを取得しようとすると、デッドロックが発生します。 デッドロックがWPFなどの環境でどのように表示されるかはわかりません。 –

+1

@DonBox:ライターのロックを取ってから、 'await Task.Delay(10000);を実行して10秒経過する前にもう一度それをクリックするだけです。 –

関連する問題