C # имеет довольно хорошую модель коллекции, но класс ReadOnlyCollection является одним из наиболее к сожалению задуманных (или именованных) классов во всей модели. Его лучше было бы назвать списком только для чтения, а не коллекцией только для чтения.
Теперь, чтобы перейти к вашему вопросу, это просто декоратор, доступный только для чтения, из IList, поставляемого во время сборки. Поэтому код, создавший коллекцию ReadOnlyCollection, может изменить исходный список со всеми вытекающими последствиями для многопоточного доступа.
Таким образом, перечисление через коллекцию было бы поточно-ориентированным, если бы коллекция действительно была доступна только для чтения; но так как он не только для чтения, он не является потокобезопасным. Учитывая количество вашей репутации, я уверен, что вам не интересно, почему перечисление через коллекцию, не предназначенную только для чтения, не является потокобезопасным.
Что касается предложенного вами обходного пути, вы можете либо использовать блокировку, либо использовать принцип «ничего не блокировать» (или блокировать как можно меньше) и делать истинную копию только для чтения. список.
EDIT
Я перечитываю свой ответ много месяцев спустя (благодаря комментарию Асинквейта) и понимаю, что должен был ответить на все вопросы ОП, не делая предположений на основании его репутации. ОП уже получил свой ответ, но я сделаю это ради будущих читателей.
Перечисление через коллекцию, не предназначенную только для чтения, изначально не является поточно-ориентированным по тем же причинам, по которым даже в однопоточном сценарии вы не можете изменять коллекцию при ее перечислении. (ConcurrentModificationException в Java, InvalidOperationException в C #.) В однопоточном сценарии вы можете убедиться, что ваш код перечисления не пытается каким-либо образом изменить коллекцию, но в многопоточном сценарии один поток может перечислять коллекцию, пока другой поток может изменять его одновременно.