Winforms application hangs when switching to another app - c#

I believe I have a potential threading issue. I have a user control that contains the following code:
private void btnVerify_Click(object sender, EventArgs e)
{
if (!backgroundWorkerVerify.IsBusy)
{
backgroundWorkerVerify.RunWorkerAsync();
}
}
private void backgroundWorkerVerify_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
VerifyAppointments();
}
private void backgroundWorkerVerify_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Information was Verified.", "Verify",
MessageBoxButtons.OK, MessageBoxIcon.Information);
CloseEvent();
}
vanilla code. but the issue I have is that when the application is running and the users tabs to another application when they return to mine the application is hung, they get a blank screen and they have to kill it. This started when I put the threading code. Could I have some rogue threads out there? what is the best way to zero in a threading problem? The issue can't be recreated on my machine...I know I must be missing something on how to dispose of a backgroundworker properly. Any thoughts are appreciated, Thanks,
Jose

Your code snippet doesn't explain it, but deadlocking the UI thread is never that difficult when you use BGW and are interested in its IsBusy property. A deadlock like this is usually easy to diagnose, use Debug + Break All. Then Debug + Windows + Threads and double-click the Main Thread. Then Debug + Windows + Call Stack to see what the UI thread is doing.
The common scenario is that the UI thread is looping on the IsBusy property. The BGW can't complete because its RunWorkerCompleted event can't run until the UI thread goes idle.

Are you accessing the GUI from the VerifyAppointments() method? You should utilize the DoWorkEventArgs in order to pass in the arguments you are verifying and you should not access the GUI from the BackgroundWorker directly.
You can safely access the GUI only in the RunWorkerCompleted or the ProgressChanged events.

Related

Background worker does not Work WPF

In my WPF program it took huge processing time and freezing for long time.
so I decided to use background worker and process it in background.
but it does not work. through debug, the program stop at Render3D(). It does not throw exception. Its like when you put return.
In other word it does nothing after reaching Render3D() and will just return.
(I don't say it will return Because im not sure but the behavior is same as return)
private readonly BackgroundWorker backgroundWorker = new BackgroundWorker();
private AssetDeclaration _assetDeclaration = new AssetDeclaration();
public MainWindow()
{
backgroundWorker.DoWork += backgroundWorker1_DoWork;
backgroundWorker.ProgressChanged += backgroundWorker1_ProgressChanged;
backgroundWorker.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
InitializeComponent();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 1000; i++)
{
if (!((BackgroundWorker)sender).CancellationPending)
{
Render3D(); // will return at this point. (why?) or waiting for something to start?
((BackgroundWorker)sender).ReportProgress(i);
}
else
{
e.Cancel = true;
break;
}
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Done!");//will show message box instant.
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
ProgressBar1.Value = e.ProgressPercentage;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//...Some work here before starting Hard job!
//...From now i want to start heavy process in background.
//...with report to progress bar at same time.
backgroundWorker.RunWorkerAsync(100);
}
Render3D() works fine without Background processing but will freeze for some time.
Render3D() is in Partial class of MainWindow .because there are lots of methods so i decided to separate them.
Also how can I use ReportProgress outside backgroundWorker1_DoWork . for example in Render3D().
Last thing : i want to know how to show the user how much of process is done.
Solved!:
The problem was because i set Viewport3D inside Render3D()
I separated it from Render3D and problem got fixed. thanks to Henk Holterman for the right answer.
It seems some tasks cant be done in another Thread. with the Error report i find out that the invalid task is setting Viewport3D properties.
this tasks must be done in Main thread.
below is invalid Code that made background worker stop functioning.
DefineCamera();
Viewport.Children.Add(model); // Must be run in Main thread.
And this Part.
private void DefineCamera()
{
PerspectiveCamera camera = new PerspectiveCamera
{
FieldOfView = 60
};
PositionCamera(camera);
Viewport.Camera = camera; // Must be run in Main thread.
}
First of all, you had trouble finding the error.
... the program stop at Render3D(). It does not throw exception. Its like when you put return.
What actually happened was that an exception was thrown by your method and was captured by the Backgroundworker. It is transferred to the Completed event but you do have to act on it there.
private void worker_Completed(object sender, RunWorkerCompletedEventArgs e)
{
// check error, check cancel, then use result
if (e.Error != null)
{
// handle the error
}
else if (e.Cancelled)
{
// handle cancellation
}
else
{
// use the result(s) on the UI thread
}
// general cleanup
}
Failing to look at either e.Error or e.Result is the same as having an empty catch{} block in your program.
And with error handling in place we then have
oh yes it shown Error. System.InvalidOperationException the calling thread cannot access this object because a different thread owns it
This indicates that your Render3D() still interacts with the GUI somewhere.
The basic advice is to separate all the calculation (and I/O, database) work from the UI work. You can run the CPU bound and I/O bound cod in a thread but the GUI is single threaded, you can only interact with it from the main Thread.
In the world of WPF, unlike Windows Forms that you were used to, you should consider Dispatcher. To do this, you have to import System.Windows.Threading
private void ThreadTask()
{
Thread.Sleep(TimeSpan.FromSeconds(5));
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
(ThreadStart)delegate()
{
//Do some heavy task here...
});
}
Quick Update
In order to run the thread from a button click or whatever, any function, add this line of code:
Thread thread = new Thread(new ThreadStart(ThreadTask));
thread.Start();
This line of code is equivalent to BackgroundWorker.RunWorkerAsync();
I would highly recommend using async/await. This feature was introduced in .NET 4.5, and is used to shift work off the main WPF GUI thread to make the application fast and responsive.
Essentially, the rule is to push any calculations which do not interact with GUI onto a background thread using a combination of Task.Run and async/await. Together with Dispatcher.Invoke, you don't really need anything else.
For example, a slow data call that might fetch data from the database could be pushed onto a background thread, so the application does freeze while it waits for the SQL to execute.
I've used this to make the applications that I write fast, responsive and snappy.

Enable button clicking while another process is running

I look trough google a bit but cant make it work.
I have some process done when i push a button.
I want to add a "Kill All" button to terrminate everying when pushed, but when i start a process i cant push any other button untill its finnished.
private void button_checkZero_phones_Click(object sender, EventArgs e)
{
Thread thread = new Thread(new ThreadStart(WorkThreadFunction));
thread.Start();
}
private void button_kill_all_Click(object sender, EventArgs e)
{
System.Environment.Exit(1);
}
You have to run your "Kill All" method in an async method. The main thread is always blocked when you run a long process.
More info about your problem:
WinForm Application UI Hangs during Long-Running Operation
Just use one of the following methods - run Kill All in:
A BackgroundWorker
Another thread
Another task
Use async/await
Reactive extensions
There are a several ways to notify you that the all processes are terminated! It depends on which method you use.
Use async funcations. I found these useful links
http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx
http://msdn.microsoft.com/en-us/library/vstudio/hh156513.aspx
Is this a Windows app? If you're feeling old-school and enjoy winding up other developers, stick an Application.DoEvents() in your loop of death.
You know you want to.
Of if you want to do it without receiving scorn from all your peers, see this question and answer.

BackgroundWorkerCompleted doesnt seem to run in main thread?

I'm writing a addin for outlook which has some network code for API calls, which is why I have several classes extending the BackgroundWorker class, each encapsulating a API call. The code looks like this for a Api Call:
public class ApiLogin : BackgroundWorker
{
private void ThisAddInStartup(object sender, EventArgs e)
{
this.DoWork += BgWorkerDoWork;
this.RunWorkerCompleted += BgWorkerCompleted;
}
private void BgWorkerDoWork(object sender, DoWorkEventArgs e)
{
//Perform network call on the background thread
var logged_in = ApiRequests.login();
e.Result = logged_in;
}
//This should run in the main thread, no?
private void BgWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
var logged_in = (bool)e.Result;
//Do stuff in main thread, hopefully..
//Investigate if this runs in the main thread since it should block Outlook, no?
Thread.Sleep(50000);
}
}
And the code looks like this for my Outlook Addin:
public class ThisAddin
{
private ApiLogin _loginWorker;
private void ThisAddInStartup(object sender, EventArgs e)
{
_loginWorker = new ApiLogin();
_loginWorker.RunWorkerAsync();
}
}
When I run my addin I expect outlook to block for 50 seconds since I have a Thread.Sleep(50000) in the background workers Completed-event handler, but this does not happen. This implies to me that this code does not run in the main thread? I have searched for a solution in vain and now I would like to know if anyone here knows what might be the problem?
BackgroundWorker requires a synchronization provider to determine on which thread the RunWorkerCompleted event runs. It uses SynchronizationContext.Current. Odds are very high that this property is null when your plugin starts. So nothing is getting synchronized and the event runs on a threadpool thread.
There are two synchronization providers in the .NET framework, respectively the one for Winforms and the one for WPF. They need their respective message loop to do the thread marshaling, they assign SynchronizationContext.Current in their Application.Run() method. You have neither. The simplest solution is to create a Winforms Form and call its ShowDialog() method. That in itself already blocks the Outlook user interface. Also good to provide some feedback to the user so she doesn't have to guess why Outlook stopped responding.
I would certainly expect it to run on the same thread as the ThisAddinStartup method: you can verify this by tracing the thread id in both places.
As for Outlook, maybe it's running your add-in on a separate UI thread.

c# calling backgroundWorker from another thread than UI thread

I'm trying to load loadingForm like below code. But it doesn't work, the loadingForm doesn't disappear, the event RunWorkerCompleted doesn't get called.
And also, I need to call loadingForm and backgroundWorker multiple times, so how do I completely dispose the loadingForm and the backgroundWorker after each call?
I think that there're many things wrong in my code but I don't know how to fix it. Could you show me how to solve my problem and point out where I need to fix? Thanks a lot in advance.
public partial class loginForm : Form
{
//....
private loadingForm lf;
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
lf.Show();
While (backgroundWorker1.isBusy)
Application.DoEvents();
}
private void backgroundWorker1_RunWorkerCompleted(object sender, DoWorkEventArgs e)
{
lf.Close();
}
private void connect()
{
//....
Thread mainThread = new Thread(ThreadStart(listentoServer));
mainThread.Start();
}
private void listentoServer()
{
//....
lf = new loadingForm();
backgroundWorker1.RunWorkerAsync();
//....
backgroundWorker1.CancelAsync();
//....
}
}
There's a lot of things wrong with your code. If you can, try to take a step back and describe what exactly you want to do.
BackgroundWorker uses the Event-based Asynchronous Pattern (EAP). As such, it requires a thread context in which to live. UI threads satisfy this requirement, but manually-created Thread instances do not (unless you install one or make the instance a secondary UI thread).
Similarly, UI components bind to a particular thread. They require an STA thread that does message pumping (e.g., Application.DoEvents).
It looks to me like you're creating a manual Thread and then creating UI components from that thread (so you know that the thread should be STA and include a message pumping loop, neither of which are in your code). Then that thread starts a BGW which does message pumping.
It's not clear what you're trying to accomplish here - maybe displaying a dialog in a separate thread?
Multiple UI threads in a WinForms app is not an officially supported scenario AFAIK, though some people have gotten it working. I've never seen a need for it, though.
According to what you have shown (which is admittedly incomplete, so this may not be the problem), you are not hooking up your event to the backgroundWorker_DoWork and backgroundWorker_RunWorkerCompleted event handlers. Somewhere (after you instantiate your backgroundWorker), you should have this:
backgroundWorker.DoWork += new EventHandler(backgroundWorker_DoWork);
backgroundWorker.RunWorkerCompleted += new EventHandler(backgroundWorker_RunWorkerCompleted);
As a disclaimer, this was written by hand, so the event names or EventHandler types may be incorrect.
i really don't know how to fix your code definitively, or if your code even works the way you have it, i can only give you the following guidance.
use CancellationPending property of background worker, not the IsBusy property
when working with windows forms and threaded code, always use the Invoke/BeginInvoke methods to make sure you marshal your call back to the thread that the control originated from.

Invoke a method on the main thread without a WinForm control to call Invoke or BeginInvoke on

I want to run an operation on a background thread. When it has completed I want to check for any errors that occurred and re-throw them on my original thread.
I am using a backgroundworker. Throwing an exception in the RunWorkerCompleted event handler results in an unhandled exception - this makes sense if the eventhandler is running on the background thread. If I had a winform control I could call Invoke or BeginInvoke but I do not have a winform control in this object, although it is a winform project.
How can I re-throw an exception that occurred in the backgroundworker?
private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
// I want to throw an exception here, without causing an unhandled exception and without being able to call Invoke or BeginInvoke on a WinForm control.
}
else if (e.Cancelled)
{
// Do something useful
}
else
{
if (e.Result != null)
{
// Do something with the result
}
}
}
I would have assumed that the RunWorkerCompleted event handler would be running on the original calling thread. Perhaps the backgroundworker is not what I need in this case.
It's not possible to inject code into another running thread. Not even the operating system can do this.
Control.BeginInvoke works by putting the delegate reference in a queue and then using PostMessage to post a user-message into the UI thread's message queue. The Application.Run message loop looks for this message and when it finds it pops the delegate off the queue and executes it.
The point is that there is no other way to do what you need without your main thread being coded to look for a some kind of signal (or message) from the other thread.
Added
You stated that this is a WinForm application but you do not have a Control to use BeginInvoke with.
Edit: I suggested a lazy-load without thinking it through. The Control might end up getting created on the wrong thread.
Pre-create a Control prior to Application.Run that lives for the lifetime of the app. You can use this to BeginInvoke from.
Edit #3
So then I try this to make certain it works and of course it doesn't. You can't simply create a generic Control, it must have an HWND handle. Simple fix: create it like this:
invokerControl = new Control();
invokerControl.CreateControl();
That will allow you to BeginInvoke from it, even if there are no open Form objects to invoke from.
You can check from other side. I mean - place timer (that will run in same main thread as form) on your form, and once per second - check some Exception field on your form (with lock()), and also some object field to detect that operation is completed. And then from bgw_RunWorkerCompleted wrap code with try...catch, and on catch (again with lock()) set Exception field of form to caught exception. But why not use Invoke or BeginInvoke?
If you didn't create the BGW instance on the UI thread then its RunWorkerCompleted event is going to run on an arbitrary threadpool thread. Any exception you throw on that thread is uncatchable and will terminate your app with a last gasp through AppDomain.UnhandledException.
In this case, there just isn't much use for BGW anymore. It is only nice to ensure that its events run on the UI thread. You might as well use MethodInvoker.BeginInvoke(). You'll need to think this through a bit and decide exactly what you're going to do when a bit of code off on some worker thread fails to do its job. Dealing with such a mishap is generally not possible and letting the program crash and burn is the right thing to do.
If you do want some kind of way to notify the user and try to keep the program stumbling along then you really ought to create the BGW instance on the UI thread. And use, say, MessageBox.Show() in the RunWorkerCompleted event handler. Be sure to recover your program state when you do this, you almost certainly need a catch clause in DoWork() to clean up the shrapnel.
Don't throw an exception.
Raise an event in the background worker which your main application thread subscribes to and then handle the error there - by throwing an exception if necessary.
Just handle the RunWorkerCompleted event. This event is synchronized for you
BackgroundWorker bgw;
private void button1_Click(object sender, EventArgs e)
{
bgw = new BackgroundWorker();
bgw.DoWork +=new DoWorkEventHandler(bgw_DoWork);
bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
bgw.RunWorkerAsync(bgw);
}
void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
textBox1.Text = e.Error.Message;
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
throw new NotImplementedException();
}
If by chance you're using 4.0 you can switch to using a Task instead which will do what you want. See this example

Categories