I'm relatively new to WPF. I'm examining some code that looks like this:
private void button_Click(object sender, RoutedEventArgs e)
{
//Queue on dispatcher in the background so it doesn't make the UI slow
Dispatcher.BeginInvoke(new dMyDelegate(PerformOperation), DispatcherPriority.Background);
}
From the comment, I'm guessing the original code felt that this was necessary to make the UI more responsive, however, my understanding is that Dispatcher.BeginInvoke simply runs something on the UI thread. Since the buttn_Click is already on the UI thread, what's the point? Perhaps I'm misunderstanding Dispatcher and BeginInvoke. I'm assumming that Dispatcher in this case, is the dispatcher owned by the class this method is in, which is MainWindow.xaml. Can someone enlighten me?
Thanks
Well, it's asking for "background" priority, so it's only going to get executed when any more important events have been processed... If this is part of a big screen refresh, it'll effectively wait until all of that has happened before executing. Even so, if it's going to be doing anything long-running (or making any potentially blocking calls) then you're right, it really shouldn't be running on the UI thread at all.
Related
Im reading a WPF book and I see this code:
private void bgw1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
int percenti;
percenti = e.ProgressPercentage;
progressBar1.Value = percenti;
}
The question is simple.
if
ProgressBar belongs to UI Thread and
BackGroundWorker works with a Background Thread
Why are there no errors (like: The calling thread cannot access this object because a different thread owns it.)?
thanks.
Why are there no errors (like: The calling thread cannot access this object because a different thread owns it.)?
This is one of the main advantages to using BackgroundWorker. The BackgroundWorker component automatically marshals calls for progress and completion back to the synchronization context (thread) that starts the job.
In this case, that means that the event handlers for ProgressChanged (and the completion event) happen on the WPF UI thread.
The BackgroundWorker handles the thread context switch for you. The event BackgroundWorker.ProgressChanged will be raised on the UI-Thread and hence your callback bgw1_ProgressChanged will be called in the context of the UI-Thread.
This was the main purpose for the BackgroundWorker's existence: To make async work easy and straight forward in conjunction with a UI.
BackgroundWorker exists since .NET 1.0. Now that we live in 2012, we have the Task class and the Task Parallel Library and soon the c# async keyword as general means for everything async, wich makes the BackgroundWorker kind of obsolete or at least old school.
It is because you cannot make changes in the Do_work method of the background worker.
progress_changed event keeps updating what is happening in the other thread.
Bes to clear your concept through this link-->
[https://www.codeproject.com/Articles/99143/BackgroundWorker-Class-Sample-for-Beginners][1]
i recently acquired some source code for a console wrapper for a server. The program was originaly in WPF and part of the code was:
private void ServerProc_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
Dispatcher.Invoke(new Action(() =>
{
ConsoleTextBlock.Text += e.Data + "\r\n";
ConsoleScroll.ScrollToEnd();
}));
}
private void ServerProc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
Dispatcher.Invoke(new Action(() =>
{
ConsoleTextBlock.Text += e.Data + "\r\n";
ConsoleScroll.ScrollToEnd();
ParseServerInput(e.Data);
}));
}
Its also had this annotation in both voids:
// You have to do this through the Dispatcher because this method is
called by a different Thread
However in WinForms there is no such thing - is there a way to change this to a Background worker or something (Ive barely done any multi-threading)?
Both methods are event handlers. Chances are they are from some kind of listening code and I would expect that they are called from a non UI thread (eg normally a threadpool thread that is doing the listening). You can check that by putting a break point and looking at the threads window in the debugger.
So you will need to apply the winforms way of updating the UI from a non UI thread.
If you search SO you should find quite a lot on how to do that. E.g
Updating UI from a different thread
How to update GUI from another thread in C#?
Some background: A process that is running in a thread other than the UI thread is not allowed to access any UI controls directly. Your WPF ServerProc is running in a different thread than your UI which requires the Dispatcher to help you communicate from the ServerProc thread back to the UI controls in your UI thread.
If your ServerProc -- in WPF or WinForms -- were running in the UI thread, you would not need to surround it with the Dispatcher.Invoke call.
For you, you can put your ServerProc in a BackgroundWorker (MSDN example). Your DoWork method would contain the meat of the code doing the work and then depending on how the ServerProc does its work, you might be able to use ProgressChanged to do what both your sample WPF methods are doing. ProgressChanged has a parameter passed in that you would indicate if it were an error or data has been received and inside the function you could display the appropriate info. Take a look at the MSDN docs because they have a good example.
What's important to point out is that ProgressChanged happens on the UI thread so you do NOT need to surround your calls to your UI controls with Invoke; just call them normally. Same goes for RunWorkerCompleted which may be the other option for displaying data when your ServerProc has finished its job.
Finally, if you actually had to access a UI control from within your thread, you do something very similar looking as your WPF code sample. Look at MethodInvoker. Instead of Dispatcher, you're really just calling it from your main Form.
I have a simple silverlight Navigation Application.
I need to start a Storyboard when a Page is navigated on a content frame.
At first everything seemed to work fine. But now I need to do some treatment (that takes about 3 seconds) when the Page loads, just before showing the Storyboard.
Now I do not see my Storyboard animation anymore. Even if the Storyboard is started after my data is loaded.
I can emulate my problem using a Thread.Sleep like that it does the same thing :
private void Page_Loaded(object sender, RoutedEventArgs e)
{
System.Threading.Thread.Sleep(3000);
Storyboard1_Test.Begin();
}
Triggering UI changes from an event handler sometimes have this kind of issues. Usually using Dispatcher.BeginInvoke method solve it.
So just try lunching Storyboard1_Test.Begin(); from the Dispatcher.BeginInvoke
Some code (hope you don't mind i'm using VB.NET):
Dispatcher.BeginInvoke(Sub()
Storyboard1_Test.Begin()
End Sub)
By using the dispatcher.BeginInvoke you verify that the job is excuted on the UI thread when the thread is available.
a bit of a juvenile question...
I realise that in a Winforms app, long running code should be executed in its own thread. How does one accomplish this on say, a button click event?
I want to do this in an effort to free up the UI thread so that I can simultaneously overlay the current form with a semi-transparent modal dialog form. I've aleady created the modal dialog form with a neat loading GIF located in the centre that works perfectly on a button click event on its own.
The reason I've chosen this method, is because (1) I want to block any user interaction with the form while the code is being executed, and (2) provide the user with an indication that processing is underway (I dont know how to judge how long a particular piece of code will take to execute, hence opting for an indefinite loading indicator gif).
Also, on the topic of executing code in separate threads...should this not apply to any code, or only specifically to long-running code?
I would really appreciate any help on this matter! thank you!
One of the simplest ways is to use a BackgroundWorker component. Add a BackgroundWorker to your form, add an event handler for the DoWork event, and call the long-running function from there. You can start it in your button click event handler by calling the RunWorkerAsync method on the BackgroundWorker component.
In order to know when the operation is ready, set up a handler for the RunWorkerCompleted event.
private void Button_Click(object sender, EventArgs e)
{
myBackgroundWorker.RunWorkerAsync();
}
private void myBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// long-running operation here; will execute on separate thread
}
private void myBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// operation is ready
}
I'll answer the second half of your question (as Fredrik has already explained the BackgroundWorker):
No, it does not make sense to move a task to a separate thread unless the task is long running.
Running a task on a separate thread always incurrs extra overheads. It might take more of the UI thread's time to kick off the thread and handle the task completion then it would have to simply do the task in the first place.
Like any programming technique, you have to weigh up the costs and benefits for the particular situation.
I will attempt to answer the second part of your question, based on my own experience.
You will generally only use threads in one of three circumstances:
On operations which will block for noticeable periods of time on system calls (File/Socket IO, etc.)
On long running operations where a loss of UI responsiveness is undesirable.
With multiple long running operations, where exploiting a multi-core environment is desirable.
As Andrew Shepherd says, there are overheads for using Threads.
Threads complicate things dramatically. Never thread for the sake of threading.
In my WinForms application, I need to pop up a little custom dialog that stays on the screen for X amount of seconds and then disappears. So I use a System.Threading.Timer to invoke the _dialog.Close() method once the appropriate amount of time has elapsed. This of course means that I have to do the whole "if InvokeRequired BeginInvoke" dance which isn't really a problem.
What is a problem however is that my main thread might be off doing god knows what by the time the BeginInvoke is called. It might not get around to closing the window for quite a while. I don't need the window to close at a millisecond's notice, but within a second or so is really necessary.
So my question is how does BeginInvoke actually work itself into the main thread and how can I get around this odd limitation?
If your UI thread is busy for many seconds at a time, then:
You won't be able to close a window associated with that UI thread, without peppering your code with Application.DoEvents calls (a bad idea)
Your whole UI will be unresponsive during this time. The user won't be able to move any of the application's windows, and if the user drags other windows over the top of it and off again, you'll end up with an ugly mess while the UI waits to repaint itself.
Certainly use a System.Windows.Forms.Timer instead of a System.Threading.Timer for simplicity, but more urgently, look at fixing your code to avoid having such a busy UI thread.
UPDATE: The conclusion would seem to be that utilising ['BackgroundWorker](http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx) along with a System.Windows.Forms.Timer would be the best approach.
Best to use System.Windows.Forms.Timer for this purpose - this is precisely the sort of application it was designed for. Add one to the pop up form and start it as soon as the form is shown, then hide the form on the Tick event. This solution won't give you any threading issues because the timer runs purely on the UI thread.
Edit: If you want to move the logic outside of your popup form, then I recommend you just create an overload for the Show method within the form code that takes a timespan for its parameter and does the job of setting the Timers's interval and starting it.
Edit 2: If you're main (UI) thread is doing too much work and therefore blocking the message pump and not allowing the timer to fire, then it's the design that's the issue I'm afraid. Your UI thread should never be blocking for more than a fraction of a second. If you need to do serious work, do it in the background using a worker thread. In this case, because you are using WinForms, BackgroundWorker is probably the best option.
Create a dedicated thread and use Application.Run to create and show your form. This will start up a message pump on the second thread which is independent of the main thread. This can then close exactly when you want it, even if the main thread is blocked for any reason.
Invoke and BeginInvoke do get into the main thread by using a window message posted into that thread, waiting for it to be processed. Therefore, if the message pump of the main thread is not processing messages (e.g. busy), it will have to wait. You can mitigate this factor by calling Application.DoEvents() when doing time-consuming operations in the main thread, but that's not really a solution to the problem.
Edit: Sample from some splash screen code (the form requires no special code or logic):
private void ShowSplashInSeparateMessageQueue() {
Thread splash = new Thread(ShowSplashForm);
splash.IsBackground = true;
splash.Start();
}
private void ShowSplashForm() { // runs in a separate thread and message queue
using (SplashForm splashForm = new SplashForm()) {
splashForm.Load += AddDestroyTimer;
Application.Run(splashForm);
}
}
private void AddDestroyTimer(object sender, EventArgs e) {
Form form = (Form)sender;
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer(form.Container);
timer.Tick += delegate { form.Close(); };
timer.Interval = 5000;
timer.Start();
}
Invoke just places the delegate into the message queue of the thread you want to invoke it on. You could use the Dispatcher class to insert the delegate with a high priority, but there is no gurante that this will meet you timing constraints if the thread is doing a lot of work.
But this might be an indication that you are doing to much work on the user interface thread. Not responding for a second is a pain to a user. So you might think about moving some work out of the user interface thread.