Я нашел возможное замедление в моем приложении, поэтому у меня было бы два вопроса:
Нет, ваш текущий сценарий небезопасен.
В частности, если коллекция изменяется во время повтора по ней, вы получите InvalidOperationException
в итерационном потоке. Вы должны получить блокировку читателя на протяжении всего итератора:
Обратите внимание, что это не то же самое, что получение блокировки чтения для каждого шага итерации - это не поможет.
Что касается разницы между блокировками чтения/записи и "нормальными" блокировками - идея блокировки чтения/записи заключается в том, что одновременно могут считываться несколько потоков, но только один поток может писать (и только тогда, когда нет ни одного читает). В некоторых случаях это может повысить производительность - но это также увеличивает сложность решения (с точки зрения его правильности). Я бы посоветовал вам использовать ReaderWriterLockSlim
из .NET 3.5, если возможно, - он намного эффективнее исходного ReaderWriterLock
, и есть некоторые проблемы с ReaderWriterLock
IIRC.
Лично я обычно использую простые блокировки, пока не доказал, что конфликт блокировок является узким местом производительности. Профилировали ли вы свое приложение еще, чтобы узнать, где находится узкое место?
Хорошо сначала про чтение итерации без блокировки. Это не безопасно, и вы не должны этого делать. Просто, чтобы проиллюстрировать суть самым простым способом - вы повторяете сборку, но вы никогда не знаете, сколько предметов в этой коллекции и не имеют способа узнать. Где вы остановились? Проверка подсчета каждой итерации не помогает, потому что она может измениться после проверки, но прежде чем вы получите элемент.
ReaderWriterLock предназначен для ситуации, когда вы разрешаете нескольким потокам иметь одновременный доступ для чтения, но принудительно синхронно записывайте. Из звуков вашего приложения у вас нет нескольких одновременных считывателей, и записи так же распространены, как и чтение, поэтому ReaderWriterLock не дает никакой пользы. В этом случае вам будет лучше служить классическая блокировка.
В целом, крошечные преимущества в производительности, которые вы вытесняете из-за неблокирующего доступа к общим объектам с многопотоковой обработкой, резко компенсируются случайной странностью и необъяснимым поведением. Заблокируйте все, что является общим, протестируйте приложение, а затем, когда все будет работать, вы можете запустить профайлер, проверьте, сколько времени приложение ожидает блокировок, а затем при необходимости реализует некоторые опасные трюки. Но шансы на то, что воздействие будет небольшим.
"Мы должны забыть о небольшой эффективности, скажем, около 97% времени: преждевременная оптимизация - это корень всего зла. Но мы не должны упускать наши возможности в этих критических 3%. Хороший программист не будет быть убаюканным до самоуспокоения такими рассуждениями, он будет разумно внимательно смотреть на критический код, но только после того, как этот код был идентифицирован" - Дональд Кнут