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

17

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

    
задан Larsenal 13.08.2008 в 22:41
источник

8 ответов

9

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

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

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

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

myControl.BeginInvoke(myControl.UpdateFunction);

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

    
ответ дан John Sibly 13.08.2008 в 22:47
источник
22
  

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

     

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

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

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

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

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

[изменить]

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

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

Ссылка

    
ответ дан Brian Ensink 14.08.2008 в 00:34
источник
7

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


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


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

    
ответ дан Björn Waide 14.08.2008 в 01:09
источник
2

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

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

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

    
ответ дан Quibblesome 13.08.2008 в 22:52
источник
1

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

Edit:

  

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

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

    
ответ дан Adam Haile 13.08.2008 в 22:44
источник
1

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

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

    
ответ дан Andrew Grant 13.08.2008 в 23:26
источник
0

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

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

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

    
ответ дан Ronnie 14.08.2008 в 00:06
источник
0

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

ответ дан Chris Hughes 13.10.2008 в 15:44
источник