WinForms: Вопрос реализации для того, чтобы мой пользовательский интерфейс работал независимо от моего слоя BLL?

Я пытаюсь сделать приложение для Windows Forms в стиле MVP и - не особо разбираясь с многопоточностью - запутываюсь.

Мой пользовательский интерфейс представляет собой набор очень простых форм. Каждая из форм реализует интерфейс и содержит ссылку на класс-посредник, который находится на уровне бизнес-логики и наоборот. Так как упрощенная схема выглядит так:

CheckInForm : ICheckIn                      <-------> CheckInMediator : ICheckInMediator
----------------------------------------------------------------------------------------
CheckInForm.Show()                          <--------
                                            --------> AttemptCheckIn(CheckInInfo)
CheckInForm.DisplayCheckInInfo(DisplayInfo) <-------- 
                                            --------> CompleteCheckIn(AdditionalCheckInInfo)
  PleaseWaitDialog.Show()                   <--------
  PleaseWaitDialog.Close()                  <--------
CheckInForm.Close()                         <--------

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

Эта архитектура приятна и отделена, и ее было очень легко протестировать и создать прототип. Теперь, когда я собираю все это вместе, я начинаю сталкиваться с проблемами потоков. Например, если я хочу, чтобы мой PleaseWaitDialog отображался в виде модальной формы (с использованием ShowDialog()) над CheckInForm, пока таймер, управляемый посредником, не отсчитал 5 секунд (помните, это упрощение), я получу ошибку перекрестного потока если я позвоню PleaseWaitDialog.Close() из обратного вызова таймера. Аналогичным образом, если у меня есть модальное диалоговое окно, блокирующее взаимодействие пользователя с пользовательским интерфейсом, я не хочу, чтобы это блокировало активность на бизнес-уровне, если я не укажу иное (например, в диалоговом окне подтверждения).

Я думаю, что хотел бы запустить посредников и бизнес-логику в основном потоке, а пользовательский интерфейс - в совершенно отдельном потоке, и мой первый вопрос: имеет ли это смысл делать?

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

2 ответа

Вы смотрели в класс BackgroundWorker? Он отлично подходит для выполнения многих упрощенных процедур в процедурах фонового типа и предоставляет события, которые можно перечислить, чтобы ваш GUI отображал прогресс.

Вы можете управлять элементами управления WinForms из другого потока, но вам нужно использовать Control.Invoke()и вы будете платить значительную потерю производительности за каждый межпоточный вызов из-за переключения контекста и связанного закулисного CLR voodoo.

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

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

Другие вопросы по тегам