Основная причина ошибки заключается в том, что конструкция List<T>
не является поточно-ориентированной.
Давайте посмотрим, что происходит при создании новой SynchronizedReadOnlyCollection
.Исключение возникает в следующей строке:
return new SynchronizedReadOnlyCollection<int>(testlist.SyncRoot, testlist);
Как сообщает StackTrace, в процессе построения задействовано List<T>..ctor
:
at System.Collections.Generic.SynchronizedCollection`1.CopyTo(T[] array, Int32 index)
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Collections.Generic.SynchronizedReadOnlyCollection`1..ctor(Object syncRoot, IEnumerable`1 list)
Следующий фрагмент из конструктора List<T>
показывает, где произошла ошибкаслучается.Код скопирован из Справочный источник MS . Я очистил ненужные части кода для удобства чтения.Обратите внимание, что между комментариями (1) и (2) существуют другие темы, управляющие коллекцией:
public List(IEnumerable<T> collection) {
ICollection<T> c = collection as ICollection<T>;
// (1) count is now current Count of collection
int count = c.Count;
// other threads can modify collection meanwhile
if (count == 0)
{
_items = _emptyArray;
}
else {
_items = new T[count];
// (2) SynchronizedCollection.CopyTo is called (which itself is thread-safe)
// Collection can still be modified between (1) and (2)
// No when _items.Count != c.Count -> Exception is raised.
c.CopyTo(_items, 0);
_size = count;
}
}
Решение
Проблема может быть легко решена с помощью блокировки testlist
модификация при строительстве нового SynchronizedReadOnlyCollection
.
public SynchronizedReadOnlyCollection<int> pubReadOnlyProperty
{
get
{
lock (testlist.SyncRoot)
{
return new SynchronizedReadOnlyCollection<int>(testlist.SyncRoot, testlist);
}
}
}