Блокировка против блокировки AcquireReader и записи - PullRequest
2 голосов
/ 01 ноября 2009

Я обнаружил возможное замедление в своем приложении, поэтому у меня возникло бы два вопроса:

  1. Какова реальная разница между простой блокировкой на объекте и блокировками чтения / записи? Например. У меня есть коллекция клиентов, которые быстро меняются. Для итераций я должен использовать ридерлок или простой блокировки достаточно?
  2. Чтобы уменьшить нагрузку, я оставил итерацию (только чтение) одной коллекции без каких-либо блокировок. Эта коллекция часто и быстро меняется, но элементы добавляются и удаляются с помощью блокировок записи. Безопасно ли (я не против случайного пропускаемого элемента, этот метод работает в цикле и не критично) оставлять это чтение незащищенным блокировкой? Я просто не хочу случайных исключений.

Ответы [ 2 ]

2 голосов
/ 01 ноября 2009

Хорошо, сначала об итерации чтения без блокировок. Это небезопасно, и вы не должны этого делать. Просто чтобы проиллюстрировать это самым простым способом - вы перебираете коллекцию, но никогда не знаете, сколько элементов в этой коллекции, и у вас нет возможности это выяснить. Где ты остановился? Проверка счетчика на каждой итерации не помогает, потому что он может измениться после того, как вы его проверите, но до того, как получите элемент.

ReaderWriterLock разработан для ситуации, когда вы разрешаете нескольким потокам иметь одновременный доступ на чтение, но принудительно выполняете синхронную запись. Судя по звукам вашего приложения, у вас нет нескольких одновременных считывателей, и записи столь же распространены, как и чтения, поэтому ReaderWriterLock не дает никаких преимуществ. В этом случае вам лучше обойтись классическим замком.

В целом, какие бы незначительные преимущества в производительности вы ни выдавали, не блокируя доступ к общим объектам с многопоточностью, они резко компенсируются случайными странностями и необъяснимым поведением. Заблокируйте все, что является общим, протестируйте приложение, а затем, когда все заработает, вы можете запустить на нем профилировщик, проверить, сколько времени приложение ожидает на блокировках, а затем, при необходимости, реализовать опасные хитрости. Но скорее всего, влияние будет небольшим.

«Мы должны забыть о малой эффективности, скажем, в 97% случаев: преждевременная оптимизация - корень всего зла. Тем не менее, мы не должны упускать наши возможности в эти критические 3%. Хорошие программисты не будут успокаиваться такими рассуждениями, он будет мудрым, чтобы внимательно посмотреть на критический код; но только после того, как этот код был идентифицирован » - Дональд Кнут

2 голосов
/ 01 ноября 2009

Нет, ваш текущий сценарий не безопасный.

В частности, если коллекция изменяется во время итерации, вы получите InvalidOperationException в итерирующем потоке. Вы должны получить блокировку чтения на весь период действия вашего итератора:

  • Получить блокировку считывателя
  • Перебор коллекции
  • Снять блокировку считывателя

Обратите внимание, что не - это то же самое, что получение блокировки чтения для каждого шага итерации - это не поможет.

Что касается разницы между блокировками чтения / записи и «нормальными» блокировками - идея блокировки чтения / записи заключается в том, что несколько потоков могут читать одновременно, но писать может только один поток (и только когда никто читает). В некоторых случаях это может улучшить производительность, но также увеличивает сложность решения (с точки зрения правильного решения). Я бы также посоветовал вам использовать ReaderWriterLockSlim из .NET 3.5, если это возможно - это намного эффективнее, чем оригинальный ReaderWriterLock, и с ReaderWriterLock IIRC есть некоторые присущие проблемы.

Лично я обычно использую простые блокировки, пока я не доказал , что конфликт блокировок является узким местом производительности. Вы уже профилировали свою заявку, чтобы узнать, где находится узкое место?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...