2010-09-25 3 views
19

Я знаю, что если я изменяю элемент управления из другого потока, я должен позаботиться, потому что WinForms и WPF не позволяют изменять состояние управления из других потоков.Почему только пользовательский интерфейс разрешает изменять пользовательский интерфейс?

Почему это ограничение на месте?

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

+0

Я считаю, что одна из причин, по которой это ограничение существует, состоит в том, что всегда существует не более одного потока, гарантированного прослушиванием цикла сообщений Windows. –

+1

@Tim: Это неправильно, существует несколько потоков пользовательского интерфейса, каждый из которых имеет свой собственный цикл сообщений. Например, разрешено разворачивать второй поток, чтобы отобразить диалог индикатора выполнения. –

+0

@Ben: этот пост отмечен C#. В приложении .Net вы всегда должны маршировать управление обратно в поток GUI для выполнения работы, связанной с рисованием (или рискованного поведения или исключения). Конечно, это не мешает вам создавать любое количество потоков для выполнения фоновой работы. Что касается прослушивания входящих сообщений Win32, единственный способ, которым я это знаю (кроме внешнего вызова), - это «защищенный переопределить void WndProc (ref Message m)». Теоретически, может принимать сообщения, переданные этому методу, и использовать их в любом потоке. –

ответ

19

Несколько графических интерфейсов имеют такое ограничение. Согласно книге Java Concurrency in Practice причина этого заключается в том, чтобы избежать сложной блокировки. Проблема в том, что элементы управления графическим интерфейсом, возможно, придется реагировать на оба события из пользовательского интерфейса, привязки данных и т. Д., Что приводит к блокировке из нескольких разных источников и, следовательно, к риску взаимоблокировок. Чтобы избежать этого, .NET WinForms (и другие пользовательские интерфейсы) ограничивает доступ к компонентам в один поток и, таким образом, позволяет избежать блокировки.

+0

Добавьте к этому совместимость с элементами управления ActiveX (управление Brower и т. Д. - возможно, косвенно через интеграцию winforms), и у вас также есть COM-наследие, которое является однопоточным Appartement. – TomTom

+1

Windows ** не ** ограничивает доступ к потоку владения. Это ограничение вводится .NET. –

+0

@Ben: Я этого не знал. Я обновлю свой ответ, чтобы это отразить. Я проверю, что говорит книга. –

3

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

8

В случае окон, когда создается элемент управления Обновления пользовательского интерфейса выполняются с помощью сообщений от насоса сообщений. Программист не имеет прямого управления потоком, на котором работает насос, поэтому приход сообщения для управления может привести к изменению состояния элемента управления. Если другому потоку (который программист находился под прямым контролем) было позволено изменить состояние элемента управления, тогда необходимо было бы создать некоторую логику синхронизации, чтобы предотвратить повреждение состояния управления. Элементы управления в .Net не являются потокобезопасными; это, я подозреваю, по дизайну. Помещение логики синхронизации во всех элементах управления было бы дорогостоящим с точки зрения проектирования, разработки, тестирования и поддержки кода, который обеспечивает эту функцию. Программист мог, конечно, обеспечить безопасность потока для управления для своего кода, но не для кода, который находится в .Net, который работает одновременно с его кодом. Одним из решений этой проблемы является ограничение этих типов действий только на один поток и только один поток, что упрощает управление управляющим кодом в .NET.

1

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

Другим интересным аспектом аффинности с управляющей нитью является то, что он может (хотя я подозреваю, что они никогда не будут) использовать шаблон Thread Local Storage. Очевидно, что если вы получили доступ к элементу управления, отличному от того, на котором он был создан, он не сможет получить доступ к правильным данным TLS независимо от того, насколько тщательно вы структурировали код для защиты от всех обычных проблем многопоточного кода.

1

На самом деле, насколько я знаю, это был план с самого начала! Доступ к любому элементу управления можно получить из любого потока! И только потому, что блокировка потоков была необходима, когда другой поток требовал доступа к элементу управления - и потому, что блокировка дорогая - была создана новая модель потоковой обработки, называемая «аренда потоков». В этой модели связанные элементы управления будут объединены в «контексты», используя только один поток, тем самым уменьшая необходимое количество блокировки. Довольно круто, да?

К сожалению, эта попытка была слишком смелой, чтобы преуспеть (и немного сложнее, потому что блокировка по-прежнему требуется), поэтому хорошая старая модель потоковой обработки Windows Forms - с единственным потоком пользовательского интерфейса и с создающей нитью, требующей права собственности на контроль - снова используется в wPF, чтобы сделать нашу жизнь ... проще?

1

Windows поддерживает множество операций, которые, в частности, используются в комбинации, по своей сути не являются потокобезопасными. Например, если один поток пытается вставить какой-либо текст в текстовое поле, начинающееся с 50-го символа, а другой поток пытается удалить первые 40 символов из этого поля? Было бы возможно, чтобы Windows использовала блокировки, чтобы гарантировать, что вторая операция не может быть начата до тех пор, пока первая не будет завершена, но использование блокировок добавит накладные расходы для каждой операции, а также повысит вероятность взаимоблокировки, если действия на одном объекте потребуют манипуляции с другим. Требование о том, что действия, связанные с конкретным окном, должны происходить в конкретном потоке, является более жестким требованием, чем это необходимо для предотвращения одновременного выполнения небезопасных комбинаций операций, но его относительно легко анализировать. Использование элементов управления из нескольких потоков и предотвращение столкновений с помощью некоторых других средств обычно будет более сложным.

Смежные вопросы