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

Я уверен, что для этого есть хорошая (или по крайней мере достойная) причина. Что это?

Ответ 1

Потому что вы можете легко завершить тупик (среди прочих проблем).

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

В других языках, таких как С++, вы можете попробовать и сделать это (без исключения, как в WinForms), но ваше приложение может зависнуть и перестать отвечать на запросы, если произойдет тупик.

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

myControl.BeginInvoke(myControl.UpdateFunction);

Это эквивалентно выполнению С++/MFC PostMessage из рабочего потока

Ответ 2

Я думаю, что это блестящий вопрос - и я думаю, что есть необходимость в улучшении Ответ.

Конечно, единственная причина в том, что там что-то в каркасе где-то что не очень безопасно для потоков.

Это "что-то" - это почти каждый член экземпляра на каждом отдельном элементе управления в System.Windows.Forms.

Документация MSDN для многих элементов управления в System.Windows.Forms, если не все из них, говорит: "Все публичные статические (Shared in Visual Basic) члены этого типа являются потокобезопасными. Любые члены экземпляра не гарантируются как поток сейф".

Это означает, что члены экземпляра, такие как TextBox.Text {get; set;}, не являются реентеративными.

Предоставление каждому из этих членов потоков потоков безопасности может привести к большим накладным расходам, которые не требуются большинству приложений. Вместо этого разработчики .Net framework решили, и я думаю, правильно, что бремя синхронизации доступа к элементам управления формами из нескольких потоков должно быть наложено на программиста.

[изменить]

Хотя этот вопрос задает только "почему", вот ссылка на статью, в которой объясняется "как":

Как сделать безопасные вызовы для Windows Forms Controls в MSDN

http://msdn.microsoft.com/en-us/library/ms171728.aspx

Ответ 3

Хотя звучит разумно, ответ Джонса неверен. Фактически даже при использовании Invoke вы по-прежнему небезопасны, чтобы не работать в ситуациях блокировки. При работе с событиями, запущенными в фоновом потоке с использованием Invoke, может даже привести к этой проблеме.


Настоящая причина больше связана с условиями гонки и возвращается в древние Win32 раз. Я не могу объяснить подробности здесь, ключевые слова - сообщения, события WM_PAINT и тонкие различия между "ОТПРАВИТЬ" и "POST".


Дополнительную информацию можно найти здесь здесь и здесь.

Ответ 4

В версии 1.0/1.1 во время отладки не было выбрано исключение, вместо этого вы использовали прерывистый сценарий зависания во время выполнения. Ницца!:) Поэтому с 2.0 они сделали этот сценарий исключительным и совершенно справедливым.

Фактической причиной этого является, вероятно, (как утверждает Адам Хейл) какая-то проблема concurrency/locky. Обратите внимание, что обычный .NET api (такой как TextBox.Text = "Hello";) обертывает команды SEND (которые требуют немедленного действия), которые могут создавать проблемы, если они выполняются в отдельном потоке от того, который выполняет обновление. Использование Invoke/BeginInvoke вместо этого использует POST, который ставит в очередь действие.

Подробнее о SEND и POST здесь.

Ответ 5

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

Edit:

В других языках, таких как С++, вы чтобы попытаться сделать это (без исключение выбрасывается, как в WinForms), но вы закончите обучение трудный путь!

Ahh да... Я переключаюсь между C/С++ и С# и поэтому был немного более общим, тогда мне следовало бы, извините... Он прав, вы можете сделать это на C/С++, но это будет вернись, чтобы укусить тебя!

Ответ 6

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

Некоторые API-интерфейсы предоставляют возможность изменить текущую принадлежность потока к системе, чтобы вы могли временно (или постоянно) обновлять системы из других потоков, не прибегая к межпоточной коммуникации.

Ответ 7

Я думаю, что это блестящий вопрос - и я думаю, что нужен лучший ответ.

Конечно, единственная причина в том, что в фреймворке есть что-то, что не очень безопасно для потоков. Это проблема с .NET или Win32 - и почему нет толчка для исправления источника, а не для обеспечения того, что для меня похоже на неприятную работу?

Кто-нибудь знает, где находится основная проблема?

Ответ 8

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