Время и время снова мне приходится писать поточно-безопасные версии BindingList и ObservableCollection, потому что, привязанные к UI, эти элементы управления не могут быть изменены из нескольких потоков. То, что я пытаюсь понять, - это , почему это так - это ошибка дизайна или это намеренное намерение?
Почему классы, подобные BindingList или ObservableCollection, не являются потокобезопасными?
Ответ 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>
, который автоматически уведомляет об этом поток пользовательского интерфейса. Тем не менее, все равно будет безопасно только для одного потока делать обновления и т.д. За раз.