ReaderWriterLock.UpgradeToWriterLock не генерирует исключение по истечении времени ожидания?

1

Я наткнулся на действительно странную проблему с ReaderWriterLock при написании модульного теста. Я попробовал тестировать метод UpgradeToWriterLock с параметром таймаута, установленным в 50 миллисекунд.

В основном потоке я беру блокировку читателя, а затем запускаю множество задач. В задачах я также беру считыватель, а затем пытаюсь выполнить обновление до автора с тайм-аутом. Это должно терпеть неудачу на каждом из них, так как основной поток удерживает блокировку чтения. Поскольку время составляет 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();
    }

Интересно, если я отключаю блокировку чтения основного потока, прежде чем ждать задач, все работает так, как ожидалось. Исправлено правильное количество исключений таймаута.

Теги:
multithreading

1 ответ

4
Лучший ответ

Вы уверены, что все застряли, а не только последнее?

Из документации UpgradeToWriterLock:

Исключение тайм-аута не выбрасывается до тех пор, пока поток, называемый методом UpgradeToWriterLock, не сможет восстановить блокировку считывателя. Если нет других потоков, ожидающих блокировки записи, это происходит немедленно. Тем не менее, если другой поток помещен в очередь для блокировки записи, поток, который вызвал метод UpgradeToWriterLock, не может восстановить блокировку считывателя, пока все текущие считыватели не освободят свои блокировки, а один поток приобрел и освободил блокировку записи. Это верно, даже если другой поток, который запросил блокировку записи, запросил его после текущего потока, называемого методом UpgradeToWriterLock.

Обратите внимание на множество условий, которые должны возникнуть для исключения исключения таймаута. "если другой поток поставлен в очередь для блокировки записи, поток, который вызвал метод UpgradeToWriterLock, не может восстановить блокировку считывателя [и выбросить исключение] до":

  1. все нынешние читатели выпустили свои замки
  2. один поток приобрел и освободил блокировку записи

Вы никогда не позволяете этим условиям возникать в последнем потоке, который пытается обновиться, поэтому вы всегда будете ждать UpgradeToWriterLock, и поэтому WaitAll тоже WaitAll ждать. Если ваш основной поток также попытался обновиться до ожидания, я думаю, что все будет в порядке.

  • 0
    Вы правы, это именно то, что происходит. Я обнаружил удивление, что это не всегда сразу выбрасывает тайм-аут. Отвечая на ваш вопрос, если я запускаю все задачи одновременно с помощью ManualResetEvent, то случайное количество задач застревает при вызове UpgradeToWriterLock.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню