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

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

7 ответов

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

    Редактировать:

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

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

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

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

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

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

    myControl.BeginInvoke(myControl.UpdateFunction);
    

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

  3. В версии 1.0 / 1.1 во время отладки не возникло никаких исключений, вместо этого вы получили прерывистый сценарий зависания во время выполнения. Здорово! 🙂
    Поэтому с 2.0 они сделали так, что этот сценарий вызвал исключение, и совершенно справедливо.

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

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

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

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

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

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

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

    Документация MSDN для многих элементов управления в системе.Windows.Формы, если не все из них, говорят: «все открытые статические (Общие в Visual Basic) члены этого типа являются потокобезопасными. Не гарантируется, что все члены экземпляра будут потокобезопасны.

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

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

    [Редактировать]

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

    Как сделать Потокобезопасные вызовы элементов управления Windows Forms на MSDN

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

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

    Настоящая причина больше связана с условиями расы и лежит в далекие времена Win32. Я не могу объяснить детали здесь, ключевые слова-насосы сообщений, события WM_PAINT и тонкие различия между «отправить» и «отправить».

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

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