#c# #winforms #multithreading #marshalling
#c# #winforms #многопоточность #сортировка
Вопрос:
Доступ к элементу управления может получить только поток, который его создал — это все, что я знаю.
- У меня есть DataGridView с источником данных, основанным на BindingList<> .
- У меня есть рабочий поток (не графический интерфейс), который выполняет некоторые сложные вычисления / сравнения / etc, А затем добавляет / редактирует объект в / в BindingList<>.
- По таймеру поток GUI обновляет себя в соответствии со списком привязок<>.
Этот код работает безупречно — до тех пор, пока я не запускаюсь в среде. В среде при вызове метода .Add() в списке привязок<> я получаю эту удобную маленькую ошибку:
An Exception has occurred
EXCEPTION : Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on.
IN METHOD : get_Handle
AT LINE : 0
CLASS : System.Windows.Forms.Control
Обратите внимание, что имя нарушаемого элемента управления является пустым… Я бы подумал, что если бы проблема была в обновлении списка привязок<>, не имело бы значения, выполнялся ли я в среде или нет. Несмотря на это, это то, что я вижу. Более того, .Add() успешно завершается, даже если генерируется исключение!!
Очевидно, что в моей производственной среде это не имеет большого значения (пока?), поскольку это происходит только в Studio; и да, я мог бы вызвать поток GUI для выполнения добавления или сохранить добавления в месте, где поток GUI сможет извлечь их позже… Я не ищу обходной путь, но больше всего меня интересует ответ на этот вопрос:
Почему ошибка появляется только в studio?
Комментарии:
1. Что вы подразумеваете под «средой»?
2. @SLaks Извините — запуск программы в Visual Studio.
Ответ №1:
Ошибка может возникнуть только в VS, если это MDA (Managed Debugging Assistant), а не исключение во время выполнения. MDA предназначены для того, чтобы сообщать вам, когда вы делаете что-то, что обычно, но не на 100% всегда, что-то, из-за чего у вас возникнут проблемы с производственным кодом (что в конечном итоге и произойдет, даже если кажется, что это работает в 99% случаев на вашей машине).
Вы должны обращаться к потоку пользовательского интерфейса для выполнения метода Add.
РЕДАКТИРОВАТЬ: быть на 100% тщательным… Без отражателя (поскольку срок действия моего истек — вы слушаете Red Gate ?!) Я предполагаю, что класс Control проверяет, находитесь ли вы в потоке пользовательского интерфейса, выдает исключение в фоновом потоке, если это не так, а затем все равно перерисовывает пользовательский интерфейс. Поскольку фоновый поток уже добавил элемент, перерисовка пользовательского интерфейса видит его и отображает в пользовательском интерфейсе, как и ожидалось, но ваш фоновый поток все еще видит исключение, и вы либо проглатываете его молча в блоке catch, либо фоновый поток завершается (что в вашем приложении, возможно, допустимо для inst. этот поток BG является потоком threadpool).
Комментарии:
1. Вы не путаете resharper и reflector? И есть
ILSpy
что является довольно хорошей альтернативой reflector.2. Спасибо за такую тщательность! Я, конечно, могу заверить вас, что не существует try-catch, который передает исключение молча — я знаю это, потому что есть try catch, который печатает исключение (вот как я поймал его в Studio), и оно не печатается в рабочей среде. Кроме того, в Studio поток действительно умирает после исключения, но в рабочей среде он просто продолжает работать! Я должен верить, что исключение не отображается в рабочей среде. Но мне нравится то, что вы говорите о том, что перерисовка в любом случае происходит из потока пользовательского интерфейса — это имеет смысл!
Ответ №2:
Ошибка появляется только в VS, потому что это своего рода диагностическое сообщение, пытающееся сообщить вам, что у вас ошибка. И в рабочей среде эти дополнительные проверки по умолчанию отключены. Таким образом, ошибка всегда присутствует, но вы получаете уведомление только во время отладки.
Я не слишком знаком с многопоточным программированием WinForms, но я предполагаю, что, поскольку вы вызываете add в этом списке привязки в другом потоке, уведомления об изменениях происходят и в этом другом потоке, и поскольку вы привязали элемент управления, он изменит свое состояние во время такого уведомления. Это означает, что доступ к элементу управления осуществляется из неправильного потока, что является ошибкой.
Ответ №3:
Я думаю, что это происходит постоянно; однако ошибки привязки обычно игнорируются молча (например, BindingSource.Ошибка данных ). Visual Studio улавливает исключения, которые обрабатываются, чтобы выделить сбой.
Наличие элемента управления с привязкой к данным (DataGridView) и коллекции, поддерживающей привязку, вызывает проблемы, если вы затем обновите его из другого потока. Природа «наблюдателя» заставляет его выполнять обновления в потоке пользовательского интерфейса. Я взломал это в прошлом, но я вроде как думаю, что вам следует полностью отключить привязку к данным и обновить вручную. И не забывайте, что ручное обновление и фоновые правки должны одновременно синхронизировать доступ, чтобы они не конфликтовали.
В зависимости от того, как точно вы выполняете привязку, вы должны иметь возможность отключить ее и вручную обновить в цикле.