9

Документация ReadOnlyCollection (Т) утверждает, что:ReadOnlyCollection <T> Потокобезопасность

A ReadOnlyCollection(Of T) может поддерживать несколько читателей одновременно, пока коллекция не изменяется. Тем не менее, перечисление через коллекцию по существу не является потокобезопасной процедурой. Чтобы гарантировать безопасность потока во время перечисления, вы можете заблокировать сбор во время всего перечисления. Чтобы обеспечить доступ к коллекции несколькими потоками для чтения и записи, вы должны реализовать свою собственную синхронизацию.

Мой вопрос касается жирной части:

  1. почему перечисления коллекции внутренне не поточно-
  2. каков возможные последствий, и
  3. что обычно используемым обходный?
+1

О, мальчик. безопасные списки в .NET слишком долго. См. некоторые предыдущие обсуждения здесь: http://stackoverflow.com/questions/550616/lock -free-stack-and-queue-in-c-sharp и/или здесь: http://stackoverflow.com/questions/66622/threadsafe-foreach-enumeration-of-lists – Radu094

ответ

7

C# имеет довольно хорошую модель коллекций, но класс ReadOnlyCollection является одним из наиболее унаследованных (или названных) классов во всей модели. Его следовало бы назвать «список только для чтения», а не сборку только для чтения.

Теперь, чтобы ответить на ваш вопрос, это всего лишь декоратор для чтения ILIST, поставляемый во время строительства. Поэтому код, который построил ReadOnlyCollection, может изменить исходный список со всеми вытекающими последствиями для многопоточного доступа.

Итак, перечисление через коллекцию было бы поточно-безопасным, если бы сборник был действительно readonly; но поскольку он не доступен только для чтения, он не является потокобезопасным. Учитывая объем репутации, который у вас есть, я уверен, что вам не интересно, почему перечисление через коллекцию, не предназначенную для чтения, не является потокобезопасной.

Что касается обходного пути, о котором вы просили, то вы можете либо использовать блокировку, либо вы можете пойти с принципом «lock-nothing» (или «lock-as-little-as-possible») и сделать подлинную копию только для чтения списка.

EDIT

Я перечитывал свой ответ много месяцев спустя, (спасибо-х asyncwait комментария,), и я понимаю, что я должен ответил на все вопросы Op без предположений, основанных на его репутации. Вероятно, ОП получил свой ответ, но я сделаю это ради будущих читателей.

Перечисление через коллекцию, не предназначенную для чтения, по своей сути не является потокобезопасной по тем же причинам, что даже в однопоточном сценарии вы не можете изменять коллекцию при ее перечислении. (ConcurrentModificationException в Java, InvalidOperationException в C#.) В однопоточном сценарии вы можете убедиться, что ваш код перечисления не пытается каким-либо образом изменить коллекцию, но в многопоточном сценарии один поток может перечислять коллекцию в то время как другой поток может изменять его одновременно.

+0

Примечание: я проверил SSCLI (ротор) исходный код ReadOnlyCollection перед тем, как написать мой ответ, чтобы убедиться, что это действительно декоратор (обертка.) –

+0

I секунда. Это просто обертка. Довольно бесполезно, если ваша коллекция модифицирована вне класса someother. Имя вводит в заблуждение в мире потоков. – asyncwait

+0

@asyncwait благодарит за ваш комментарий, это напомнило мне, что я должен добавить поправку к моему ответу. –

3

Этот MSDN article staes: «Экземпляр класса Generic ReadOnlyCollection всегда доступен только для чтения.Коллекция, только для чтения просто коллекция с оболочкой, которая предотвращает изменение коллекции "

Так я считаю, итерация не поточно как он внутренне использует нормальные ноны потокобезопасных коллекций объектов.

Последствия: разные потоки могут получать разные значения, если каким-то образом изменяется коллекция. Используйте оператор lock, чтобы избежать одновременного доступа к коллекции из разных потоков одновременно.

Смежные вопросы