Концепция блокировок чтения-записи проста: он позволяет нескольким потокам одновременно получать блокировки чтения, но только один поток может одновременно получать блокировки записи, поэтому это также называют блокировками, эксклюзивными для совместного использования. В C# рекомендуется использовать класс ReaderWriterLockSlim для выполнения функции блокировки чтения/записи. В некоторых случаях количество чтений объекта значительно превышает количество модификаций, и если он просто заблокирован путём блокировки, это повлияет на эффективность чтения. Если используется блокировка чтения-записи, несколько потоков могут одновременно читать объект, и он будет блокироваться только тогда, когда объект занят блокировкой записи. Проще говоря, когда поток переходит в режим чтения, другие потоки всё равно могут перейти в режим чтения, если поток хочет перейти в режим записи в данный момент, его нужно заблокировать. Пока не выйдет режим чтения. Аналогично, если поток переходит в режим записи, остальные потоки будут блокированы, независимо от того, хотят ли они записывать или читать. Существует два способа перейти в режим записи/чтения: EnterReadLock пытается войти в режим блокировки в режиме записи. TryEnterReadLock (Int32) пытается войти в режим блокировки режима чтения с опцией выбора целочисленного тайм-аута. EnterWriteLock пытается войти в состояние блокировки режима записи. TryEnterWriteLock(Int32) пытается войти в состояние блокировки режима записи, и можно выбрать время тайм-аута. Существует два способа выйти из режима записи/чтения: ExitReadLock уменьшает рекурсивное количество в режиме чтения и выходит из режима чтения, когда итоговый счёт равен 0 (нулю). ExitWriteLock уменьшает рекурсивное количество шаблона записи и выходит из режима записи, когда итоговое количество равно 0 (нулю). Вот как им пользоваться:
Вы видите, что потоки 3 и потоки 4 могут одновременно переходить в режим чтения, а поток 5 — через 5 секунд (то есть после выхода блокировки чтения потоков 3 и 4). Измените вышеуказанный код, сначала откройте 2 потока в режиме записи, а затем в режиме чтения — код выглядит следующим образом:
Результаты следующие:
Как видно, поток 3 и поток 4 входят в режим записи, но поток 3 занимает блокировку записи первым, поэтому поток 4 должен ждать 10 секунд перед входом. Потоки 5 и 6 должны занять блокировку чтения, поэтому дождитесь, пока поток 4 выйдет из блокировки записи, прежде чем продолжать. TryEnterReadLock и TryEnterWriteLock могут установить тайм-аут, при переходе к этому предложению поток будет блокироваться здесь, если блокировка может быть занята в данный момент, затем вернёт true, если время тайм-аута ещё не заняло блокировку, затем вернёт false, отказывается от занятия блокировки и продолжает выполнять следующий код напрямую. EnterUpgradeableReadLock Класс ReaderWriterLockSlim предоставляет режим чтения с возможностью улучшения, который отличается от режима чтения тем, что его также можно обновить до режима записи, вызывая методы EnterWriteLock или TryEnterWriteLock. Потому что только один поток может быть в режиме обновления одновременно. Поток, переходящий в режим апгрейда, не повлияет на поток в режиме чтения, то есть когда поток переходит в режим апгрейда, любое количество потоков может одновременно перейти в режим чтения без блокировки. Если несколько потоков уже ждут блокировки записи, запуск EnterUpgradeableReadLock блокирует до тех пор, пока эти потоки не выйдут из блокировки записи. Следующий код показывает, как обновиться до блокировки записи в режиме апгрейдируемого чтения.
Влияние блокировок чтения/записи на производительность очевидно. Следующий тестовый код:
Вышеуказанный код моделирует работу 500 задач, каждая из которых занимает поток пула потоков, из которых 20 — это потоки записи, а 480 — потоки чтения. Чтение данных занимает 10 мс и 100 мс на запись для тестирования метода блокировки и метода ReaderWriterLockSlim соответственно. Можно сделать оценку: для ReaderWriterLockSlim можно предположить, что одновременно читают 480 потоков, то он потребляет 10 мс, 20 операций записи занимают 2000 мс, то есть затраченное время составляет 2010 мс, а для обычного метода блокировки, поскольку все они эксклюзивны, то есть 480 операций чтения занимают 4800 мс + 20 операций записи 2000мс = 6800 мс. Результаты показали заметное улучшение производительности.
|