Несколько потоков пользовательского интерфейса в одном окне - PullRequest
3 голосов
/ 31 октября 2009

Мне не нужны несколько окон, каждое из которых имеет собственный поток пользовательского интерфейса, а также события, инициируемые в одном потоке пользовательского интерфейса, а не фоновые рабочие и уведомления, и ни один из этих объектов Invoke, BeginInvoke тоже.

  • Мне интересна платформа, которая позволяет нескольким потокам безопасно обновлять одно и то же окно. Что-то вроде первого потока создает три кнопки, второй поток - еще пять, и они оба могут получить к ним доступ, изменить свои свойства и удалить их без каких-либо нежелательных последствий.
  • Мне нужен безопасный многопоточный доступ к пользовательскому интерфейсу без Invoking, платформы, к которой можно обращаться к объектам пользовательского интерфейса напрямую из любого потока без появления ошибок типа «Доступ к объекту возможен только из потока, который его создал». Позволить мне выполнять синхронизацию, если потребуется, а не помешать мне получить прямой доступ к интерфейсу пользователя.

Ответы [ 8 ]

16 голосов
/ 05 ноября 2009

Я пойду, проголосую, но ... Go Go Gadget Soapbox.

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

Может быть возможно реализовать многопоточный пользовательский интерфейс для конкретного проекта. Я только говорю, что это не может быть сделано в общем случае. Это означает, что вряд ли вы найдете платформу, которая будет делать то, что вы хотите.

Суть проблемы заключается в следующем. Представьте компоненты графического интерфейса в виде цепочки (на самом деле это больше похоже на дерево, но цепочку просто описать). Кнопка подключается к раме, подключается к коробке, подключается к окну. Существует два источника событий для системы: система / ОС и пользователь. Событие системы / ОС происходит в нижней части цепочки (оконная система), пользовательское событие - в верхней части цепочки (кнопка). Оба эти события должны проходить через цепочку графического интерфейса. Если два потока отправляют эти события одновременно, они должны быть защищены мьютексом. Однако не существует известного алгоритма для одновременного обхода двойного связанного списка в обоих направлениях. Это склонно к мертвой блокировке. Эксперты по графическому интерфейсу попытались найти способы обойти проблему взаимоблокировки и в конечном итоге пришли к решению, которое мы используем сегодня, называемому Model / View / Controller, иначе говоря, один поток запускает пользовательский интерфейс.

7 голосов
/ 04 ноября 2009

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

3 голосов
/ 04 ноября 2009

Я не верю, что такая платформа существует сама по себе

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

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

Panel p=new Panel();
Button b=new Button();
WaitForControlsCreated(); //waits until the current control queue is cleared
p.Controls.Add(b);

что, вероятно, так же медленно ..

Итак, настоящий вопрос здесь, почему? Единственный «хороший» способ сделать это - просто абстрагировать invoke, чтобы можно было добавить элементы управления из потока, не являющегося пользовательским интерфейсом.

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

2 голосов
/ 21 января 2010

Примите, что любой код, обновляющий GUI, должен быть в потоке GUI. Научитесь использовать BeginInvoke ().

1 голос
/ 15 апреля 2015

Я удивлен, увидев эти ответы.

Только языковые рамки более высокого уровня, такие как C #, имеют ограничения потоков для элементов GUI.

Окна на уровне SDK контролируются приложением на 100%, и нет никаких ограничений на потоки, кроме как на незначительном уровне сложности. Например, если несколько потоков хотят записать в окно, вам нужно заблокировать мьютекс, получить контекст устройства, нарисовать, затем освободить контекст, а затем разблокировать мьютекс. Получение и освобождение контекста устройства на момент рисования должно происходить в одном потоке ... но обычно они находятся в пределах 10 строк кода друг от друга.

Нет даже отдельного потока, в который приходят сообщения Windows, независимо от того, какой поток вызывает «DispatchMessage ()», является потоком, в который будет вызываться WINPROC.

Другое незначительное ограничение потока заключается в том, что вы можете только "PeekMessage" или "GetMessage" окно, которое было создано в текущем потоке. Но на самом деле это очень незначительно, и сколько сообщений вам нужно в любом случае.

Рисование полностью отсоединено от потоков в Windows, просто мьютекс вашего DC для рисования. Вы можете рисовать в любое время, из любого места, а не только из сообщения WM_PAINT.

1 голос
/ 04 января 2010

В Windows Дескрипторы окон имеют сходство потоков . Это ограничение оконного менеджера. Это плохая идея иметь несколько потоков, обращающихся к одному и тому же окну в Windows.

0 голосов
/ 05 ноября 2009
0 голосов
/ 31 октября 2009

Исходя из того, что я угадал ваши требования, вам нужна единая форма Windows и способы асинхронного выполнения определенных подпрограмм (например, многопоточность), да?

Обычно (для случая .NET WinForms) Control.Invoke / Control.BeginInvoke используется для определенного эффекта, который, я думаю, вы хотите.

Вот интересная статья, которая может помочь: http://www.yoda.arachsys.com/csharp/threads/winforms.shtml

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...