问题
I am trying to do a Windows Forms application in an MVP style and - not having done much with threading before - am getting all confused.
My UI is a set of very simple forms. Each of the forms implements an interface and contains a reference to a mediator class which lives in the Business Logic Layer and vice versa. So as simplified diagram looks like this:
CheckInForm : ICheckIn <-------> CheckInMediator : ICheckInMediator
----------------------------------------------------------------------------------------
CheckInForm.Show() <--------
--------> AttemptCheckIn(CheckInInfo)
CheckInForm.DisplayCheckInInfo(DisplayInfo) <--------
--------> CompleteCheckIn(AdditionalCheckInInfo)
PleaseWaitDialog.Show() <--------
PleaseWaitDialog.Close() <--------
CheckInForm.Close() <--------
As you can see, the mediator classes control the UI, telling it when to display data, start up, close, etc. They even signify when a modal dialog should appear and when it should close (ie the PleaseWaitDialog above) The only thing the UI does is show data on the screen and relay input back to the mediator.
This architecture is nice and decoupled and has been super-easy to test and prototype. Now that I'm putting it all together however I'm starting to run into threading issues. For example, if I want my PleaseWaitDialog to appear as a modal form (using ShowDialog()) over the CheckInForm until a timer controlled by the mediator counts out 5 seconds (remember, this is a simplification) I will get a cross-threading error if I call PleaseWaitDialog.Close() from the timer's callback. In a similar vein, if I have a modal dialog block the user from interacting with the UI I don't want that to block activity in the business layer unless I specify otherwise (such as with a confirmation dialog).
What I think I would like to do is to run the mediators and business logic on the main thread and the UI on a completely separate thread and my first question is does this make sense to do?
My second question is, how do I do something like have a class run in a separate thread? And how do I have the two communicate? I am making my way through the reading on .NET threading but I have a deadline and some examples for how to have a class on the main thread spawn a thread containing the UI and have them objects talk to each other could really help.
回答1:
Have you looked into the BackgroundWorker class? It's great for doing a lot of the simplified processing in the background type procedures and gives events that can be listned to do have your GUI display progress.
回答2:
You can manipulate WinForms controls from another thread, but you need to use Control.Invoke()
, and you will pay a considerable performance penalty for every cross-thread call, due to the context switch and associated behind-the-scenes CLR voodoo.
If you want to segregate the GUI from the business logic and infrastructure code in a multi-threaded application, I recommend switching to a messaging model using thread-safe queues. Every time the lower layer(s) need to tell the GUI to do something, they drop a message object into a queue which the GUI elements poll periodically via a Forms.Timer
. This works particularly well for large, processor-intensive applications, because you can throttle the processing needs of the GUI updates to some extent by adjusting the update timer frequencies.
For the calls going back the other way (GUI -> lower layers), you can just call mediator methods from the GUI code, as long as those calls return reasonably quickly - you need to be very careful about delaying the GUI thread, because the responsiveness of the whole application will suffer. If you have some calls where it is difficult to return quickly enough, you can add a second queue going the other way.
来源:https://stackoverflow.com/questions/164789/winforms-implementation-question-for-having-my-ui-run-independently-of-my-bll-l