在编写单元测试时,我偶然发现了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();
    }


有趣的是,如果我在等待任务之前释放主线程读取器锁,那么一切都会按预期进行。抛出正确数量的超时异常。

最佳答案

您确定它们都卡住了,而不仅仅是最后一个?

UpgradeToWriterLock documentation


  在调用UpgradeToWriterLock方法的线程可以重新获取读取器锁之前,不会引发超时异常。如果没有其他线程在等待写程序锁定,则会立即发生。但是,如果将另一个线程排队等待写入器锁,则调用UpgradeToWriterLock方法的线程将无法重新获取读取器锁,直到所有当前读取器都释放了它们的锁,并且一个线程已经获取并释放了写入器锁。即使在当前线程调用UpgradeToWriterLock方法之后,另一个请求写入者锁的线程也请求了它,这也是正确的。


请注意,抛出超时异常必须发生的多个条件。 “如果另一个线程正在排队等待编写器锁,则调用UpgradeToWriterLock方法的线程将无法重新获取阅读器锁[并引发异常]直到”:


当前所有的读者都已释放他们的锁
一个线程已获取并释放了作家锁


您绝不允许最后一个尝试升级的线程发生这些情况,因此您将在UpgradeToWriterLock处永远等待,因此WaitAll也将永远等待。如果您的主线程也在等待之前尝试升级,我认为您会没事的。

07-26 01:08