开发者

Threads, events and the GUI

I am a bit confused about how GUI can be used in multi-threaded applications.

I hear there is a thing called the UI thread. Which I assume is my main executing thread at the startup of the application.

I also hear (though I am not 100% on this) that doing UI stuff on other (non UI) threads is a very bad idea.

So, if I create a separate thread and I want to call MyForm myForm = new MyForm(); myForm.ShowDialog(); in it, what changes do I need to make for that to be "safe"?

Also, I have had some people tell me that events are spun out on a different thread. (Though I am not sure I believe this.) If they are, then I am confused. I can open a dialog (ie myForm.ShowDialog() in an event and nothing truly horrible happens. (Maybe this depends on if the event delegate was called开发者_开发知识库 with Invoke or BeginInvoke?)


Here are a few bits of info that may help you out. What you're saying about working with UI on non UI threads isn't just a bad idea, you'll get an exception. Meaning, if you create a Form in the main thread, and then spawn off a background thread to do some processing and then want to update the Form in that background thread, it'll throw an exception. In your example though, where you create the Form in a background thread, you should be OK. I'd question your design, but it won't blow up AS LONG AS YOU ONLY TOUCH THE UI IN THAT SAME THREAD.

As for events, events handlers are executed on the same thread they were raised on. Meaning, if you have a Form on one thread that spawns off some work on another thread that raises events, but before doing so, you hook into this event on the Form thread, you need to be careful not to touch the UI directly in the event handlers, because those event handlers are being called on the background thread.

Finally, the way to correctly manipulate the UI from a background thread, is by calling Invoke and passing in a delegate that does the UI work you want. HTH


In WinForms you need to call UI-things on UI thread, you always can check on what thread you currents are getting InvokeRequired of UI-control.

void ApplyUiChanges()
{
   if(this.InvokeRequired)
   {
       this.Invoke(new Action(ApplyUiChanges));
       return;
   } 

   // UI stuff here...
}

In WPF techinic is alike. But instead of using InvokeRequired you should ask CheckAccess() of DispatcherObject (All UI-controls derive from it)

void ApplyUiChanges()
{
   if (!dispatcherObject.CheckAccess())
   {     
      dispatcherObject.Dispatcher.Invoke(DispatcherPriority.Send, new Action(ApplyUiChanges));
      return;
   }
   // UI stuff here...
}

Also you can take a look at Async CTP, which might be useful. But it's only CTP, not a release yet.

Another way to handle UI-thread communication is to use PostSharp. Write (or copy-paste) GuiThreadAttribute. After that, you'll be able to use such semantics:

[GuiThread]
void ApplyUiChanges()
{
   // UI stuff here...
}


From what I've experienced, "UI thread" is a misnomer. There isn't one single thread that handles all of the UI for an application. To keep things simple, it's generally a good idea to have UI on one thread, but nothing stops you from spawning another thread, and creating new controls on that thread and showing them to the user. What's important is that control properties are only changed on the thread it was created on. As mentioned by another person, you can see if you are currently on that thread by looking at the Control.InvokeRequired property.

If you are on a thread that isn't the one you want a new form to run on and you don't have the luxury of being on the context of a control that is created on the thread you want, then you'll have to get a reference to the System.Threading.SynchronizationContext of the thread you want it to be on (I usually achieve this by storing a reference of System.Threading.SynchronizationContext.Current from the main thread in a static variable, but this can only be done after at least one control has been created on the thread). This object will allow you to run a delegate on its home thread.

I had to do this once in a Windows application that also hosted a WCF service, and UI needed to be launched from the service, but I wanted it on the same thread as the rest of the UI.

HTH, Brian


In WinForms applications there is only a single thread which is the UI thread. You don't want to block this thread with long operations so that the UI is always responsive. Also you shouldn't update any UI elements from any thread other than the UI thread.
I always use a BackgroundWorker if I want to perform any lengthy operations from the UI. The major benefit of BackgroundWorker is that it can report progress and report that it is complete via ProgressChanged and RunWorkerCompleted. These 2 events occur in the UI thread thus you can update any UI element safely without the need to use InvokeRequired and Invoke.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜