単体テストを書いているときに、ReaderWriterLock に関する非常に奇妙な問題に遭遇しました。タイムアウト オプションを 50 ミリ秒に設定して UpgradeToWriterLock メソッドをテストしてみました。
メイン スレッドでリーダー ロックを取得し、多数のタスクを開始します。タスクでは、リーダー ロックも取得してから、タイムアウトを使用してライターにアップグレードしようとします。メインスレッドが読み取りロックを保持しているため、これはすべてのスレッドで失敗するはずです。タイムアウトは 50 ミリ秒であるため、タスクはタイムアウト例外をスローして終了する必要があります。10 を超えるタスクを開始すると、開始されません。UpgradeToWriterLock でスタックします。
誰でも説明できますか?以下ソースコード全文。
[TestMethod]
public void UpgradeLockFailTest()
{
// strangely when more than 10 threads then it gets stuck on UpgradeToWriterLock regardless of the timeout
const int THREADS_COUNT = 20;
// 50 milliseconds
const int TIMEOUT = 50;
// create the main reader writer lock
ReaderWriterLock rwl = new ReaderWriterLock();
// acquire the reader lock on the main thread
rwl.AcquireReaderLock(TIMEOUT);
// create and start all the tasks
Task[] tasks = new Task[THREADS_COUNT];
for (int i = 0; i < THREADS_COUNT; i++)
{
tasks[i] = Task.Factory.StartNew(() =>
{
try
{
// acquire the reader lock on the worker thread
rwl.AcquireReaderLock(TIMEOUT);
// acquire the writer lock on the worker thread
rwl.UpgradeToWriterLock(TIMEOUT); // <-- GETS STUCK HERE AND DOESN'T RESPECT TIMEOUT
}
finally
{
rwl.ReleaseLock();
}
});
}
// should be enough for all the tasks to be created
Thread.Sleep(2000);
try
{
// wait for all tasks
Task.WaitAll(tasks); // <-- GETS STUCK HERE BECAUSE THE TASKS ARE STUCK ON UpgradeToWriterLock
}
catch (AggregateException ae)
{
Assert.AreEqual(THREADS_COUNT, ae.InnerExceptions.Count);
}
// release all the locks on the main thread
rwl.ReleaseLock();
}
興味深いのは、タスクを待機する前にメイン スレッド リーダー ロックを解放すると、すべてが期待どおりに機能することです。正しい数のタイムアウト例外がスローされます。