ReaderWriterLock.UpgradeToWriterLock在超时后不会抛出异常

本文关键字:抛出异常 超时 UpgradeToWriterLock ReaderWriterLock | 更新日期: 2023-09-27 18:04:54

在编写单元测试时,我偶然发现了ReaderWriterLock的一个非常奇怪的问题。我尝试测试UpgradeToWriterLock方法,超时选项设置为50毫秒。

在主线程上,我取reader锁,然后启动许多任务。在任务中,我也采用reader锁,然后尝试使用超时升级到writer。当主线程持有读锁时,这应该在每个线程上都失败。由于超时为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();
    }
有趣的是,如果我在等待任务之前释放主线程读取器锁,一切都像预期的那样工作。抛出正确数量的超时异常。

ReaderWriterLock.UpgradeToWriterLock在超时后不会抛出异常

你确定他们都被卡住了,而不是最后一个?

来自UpgradeToWriterLock文档:

在调用UpgradeToWriterLock方法的线程能够重新获取读锁之前,不会抛出超时异常。如果没有其他线程等待写入锁,则立即执行此操作。然而,如果另一个线程排队等待写锁,调用UpgradeToWriterLock方法的线程不能重新获取读锁,直到所有当前的读锁都释放了它们的锁,并且一个线程已经获取并释放了写锁。即使在当前线程调用UpgradeToWriterLock方法之后,请求写锁的另一个线程请求它也是如此。

请注意,必须出现多个条件才能抛出超时异常。"如果另一个线程排队等待写锁,调用UpgradeToWriterLock方法的线程不能重新获得读锁[并抛出异常],直到":

    当前所有读取器已释放锁
  1. 一个线程已经获得并释放了写锁

你永远不允许这些条件发生在最后一个尝试升级的线程上,所以你永远等待在UpgradeToWriterLock上,所以WaitAll也永远等待。如果你的主线程在等待之前也试图升级,我想你会没事的。