Подтвердить что ты не робот

Почему классы, подобные BindingList или ObservableCollection, не являются потокобезопасными?

Время и время снова мне приходится писать поточно-безопасные версии BindingList и ObservableCollection, потому что, привязанные к UI, эти элементы управления не могут быть изменены из нескольких потоков. То, что я пытаюсь понять, - это , почему это так - это ошибка дизайна или это намеренное намерение?

4b9b3361

Ответ 1

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

if ( myCollection.Count > 0 ) {
  var x = myCollection[0];
}

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

Почему? Несмотря на то, что myCollection безопасен, нет никакой гарантии, что между двумя вызовами метода на myCollection не произойдет изменение: namedly Count и индексатор. Другой поток может войти и удалить все элементы между этими вызовами.

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

EDIT

Я расширил эту дискуссию на недавнем блоге: http://blogs.msdn.com/jaredpar/archive/2009/02/11/why-are-thread-safe-collections-so-hard.aspx

Ответ 2

Чтобы немного добавить к Jared отличный ответ: безопасность потоков не приходит бесплатно. Многие (большинство?) Коллекций используются только в одном потоке. Почему у этих коллекций есть штрафы за производительность или функциональность, чтобы справиться с многопоточным случаем?

Ответ 3

Собирая идеи из всех других ответов, я думаю, что это самый простой способ решить ваши проблемы:

Измените свой вопрос:

"Почему класс X не имеет смысла?"

to

"Каков разумный способ сделать это с помощью класса X?"

  • в вашем конструкторе классов, получите текущий вытеснитель при создании ваши наблюдаемые коллекции. Becuase, как вы указали, необходимо изменить выполняться в потоке оригинал, который не может быть основным потоком графического интерфейса пользователя. Таким образом, App.Current.Dispatcher не имеет права alwasys, и не все классы имеют this.Dispatcher.

    _dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher;
    _data = new ObservableCollection<MyDataItemClass>();
    
  • Используйте диспетчер для вызова разделов кода которые нуждаются в исходном потоке.

    _dispatcher.Invoke(new Action(() => { _data.Add(dataItem); }));
    

Это должно сделать трюк для вас. Хотя есть ситуации, которые вы могли бы предпочесть .BeginInvoke вместо .Invoke.

Ответ 4

Если вы хотите сойти с ума - здесь a ThreadedBindingList<T>, который автоматически уведомляет об этом поток пользовательского интерфейса. Тем не менее, все равно будет безопасно только для одного потока делать обновления и т.д. За раз.