Я пытаюсь сделать приложение 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, но у меня есть крайний срок и несколько примеров того, как создать класс в основном потоке, чтобы создать поток, содержащий пользовательский интерфейс, и заставить их объекты разговаривать друг с другом, это действительно может помочь.





Вы изучили класс Справочная информация? Он отлично подходит для выполнения большей части упрощенной обработки в процедурах фонового типа и дает события, которые можно перечислить, чтобы ваш графический интерфейс отображался прогрессом.
В приведенном выше примере ваш метод DisplayCheckinInfo запускает метод CompleteCheckIn с помощью параметра BackgroundWorker. Таким образом, полная обработка проверки может запускаться в отдельном потоке, а затем вы можете отслеживать ее прогресс и соответствующим образом обновлять диалоговое окно.
Вы может манипулируете элементами управления WinForms из другого потока, но вам нужно использовать Control.Invoke(), и вы будете платить значительную потерю производительности за каждый межпотоковый вызов из-за переключения контекста и связанного за кулисами Voodoo CLR.
Если вы хотите отделить графический интерфейс от бизнес-логики и кода инфраструктуры в многопоточном приложении, я рекомендую переключиться на модель обмена сообщениями с использованием потоковобезопасных очередей. Каждый раз, когда нижнему уровню (-ам) требуется указать графическому интерфейсу пользователя что-то сделать, они помещают объект сообщения в очередь, которую элементы графического интерфейса периодически опрашивают через Forms.Timer. Это особенно хорошо работает для больших приложений с интенсивной загрузкой процессора, потому что вы можете до некоторой степени ограничить потребность в обработке обновлений графического интерфейса пользователя, регулируя частоту таймера обновления.
Для обратных вызовов (GUI → нижние уровни) вы можете просто вызвать методы-посредники из кода GUI, если эти вызовы возвращаются достаточно быстро - вам нужно быть очень осторожным с задержкой потока GUI, потому что пострадает отзывчивость всего приложения. Если у вас есть звонки, на которые сложно ответить достаточно быстро, вы можете добавить вторую очередь в обратном направлении.
Вы знаете какие-нибудь статьи или исходный код, где это делается, чтобы я мог увидеть несколько практических примеров? Честно говоря, это довольно простое приложение, поэтому производительность не является большой проблемой.
Я смотрел на это, но не должен хорошо понимать, как это будет работать в этом случае. Вы можете объяснить дальше?