Безопасно ли обновлять DataTable с привязкой к данным из потока?

Для проекта, над которым я работаю, у меня есть форма с кучей компонентов DataGridView, которые должны отображать некоторые данные. Каждый DataGridView имеет свой собственный DataTable, связанный с ним. Данные, которые должны отображаться, отправляются периодически. Мое приложение должно читать эти данные, анализировать их и соответствующим образом заполнять таблицы данных. Поскольку я хочу сохранить отзывчивость формы, я реализовал прием данных (блокировку) в бесконечном фоновом воркере.

В фоновом рабочем я получаю данные и анализирую/преобразовываю их в значения, которые подходят для DataTables. Теперь вот мой вопрос: на данный момент я присваиваю эти значения непосредственно объектам DataTable. (Поэтому я делаю это из события DoWork фонового рабочего)

Мне интересно, действительно ли это. Однажды у меня было исключение индекса за пределами границ, и мне было интересно, связано ли это как-то с этим. Является ли это безопасным и рекомендуемым способом, или мне следует использовать вызовы в событии DoWork моего фонового рабочего для обновления таблиц данных?


person William W    schedule 11.07.2012    source источник
comment
Почему вы используете один поток для этого? Похоже, вы должны использовать фонового рабочего для каждого элемента управления. Вы должны делать Invokes, не совсем уверены, что делаете это, предоставьте код, если хотите получить более подробный ответ.   -  person Security Hound    schedule 11.07.2012


Ответы (3)


Нет, все свойства элементов управления .NET WinForm (это предположение), которые влияют на отрисовку элемента управления (включая значения, привязанные к элементу управления, которые могут повлиять на отрисовку), должны быть созданы в потоке, создавшем элемент управления.

Тем не менее, во многих случаях вам сойдет с рук возможность внести изменения, но поведение непредсказуемо и не рекомендуется.

В вашем конкретном случае я бы посоветовал иметь копию DataTable , с которым работает поток обработки, а затем маршалирует эту копию в поток пользовательского интерфейса (через вызов одного из ISynchronizeInvoke interface, который Control class реализует) и обновить сетку в потоке пользовательского интерфейса.

По сути, вы должны выполнить обновление DataTable, к которому привязана сетка, с копией, маршалированной из фонового потока.

person casperOne    schedule 11.07.2012
comment
Я не знаком с WinForms... но в WPF есть диспетчер, который может запускать код в потоке пользовательского интерфейса. Разве это не возможно с winforms? поместить все обновления данных в делегаты, вызываемые этим диспетчером? - person Kek; 11.07.2012
comment
@kek Да, вы можете это сделать, но то, что вы в конечном итоге делаете, вызываете фоновый поток, который затем вызывает поток пользовательского интерфейса и затем выполняет работу. Дело в том, что вы должны маршалировать данные из фонового потока в поток пользовательского интерфейса (будь то через вызов Invoke в Windows Forms или Dispatcher в WPF) после того, как фоновый поток выполнит всю тяжелую работу. - person casperOne; 11.07.2012
comment
ОК... термин маршаллинг мне непонятен... но да, вам придется сначала поработать с копией данных и скопировать их. Спасибо за разъяснение... +1 за полноту ответа - person Kek; 11.07.2012
comment
@Kek Нет проблем, и да, маршал — это общий термин для перемещения вызова и/или данных через границы, будь то потоки (фоновый и пользовательский интерфейс), управляемый/неуправляемый код (COM-взаимодействие, P/Invoke). - person casperOne; 11.07.2012

Справедливо сказать, что вы никогда не должны обновлять какие-либо элементы, связанные с пользовательским интерфейсом, из потока, отличного от пользовательского интерфейса. Хотя очень часто вы можете не увидеть никаких исключений, если вы это сделаете, это никогда не будет хорошей практикой и часто приводит к исключениям или, что еще хуже, к невидимым ошибкам.

person stevethethread    schedule 11.07.2012

Ну нет. Хотя это очень хорошая идея выполнять вычисления в фоновом потоке, обновление пользовательского интерфейса всегда должно выполняться в потоке пользовательского интерфейса.

При привязке Datatable к элементу пользовательского интерфейса вы «отдаете» право собственности на эти объекты потоку пользовательского интерфейса и больше не должны обновлять их в фоновых потоках.

person Kek    schedule 11.07.2012
comment
Однако я не обновляю пользовательский интерфейс непосредственно из потока, а только структуру, связанную с компонентом пользовательского интерфейса. То есть, если я правильно понимаю, это не имеет значения, и это все равно должно быть сделано в потоке пользовательского интерфейса? - person William W; 11.07.2012
comment
@WilliamW Обновление структуры вызовет цепную реакцию, которая вызовет повторную визуализацию элемента управления в потоке пользовательского интерфейса. Этого делать не следует. Вы должны иметь копию структуры, выполнять работу, а затем маршалировать данные в основной поток и обновлять там структуру, связанную с компонентом пользовательского интерфейса. - person casperOne; 11.07.2012