Why threading is not performing correctly in c#? - c#

Hi I have a method like below:
void Foo
{
MethodInvoker method=delegate{
frm2 frm=new frm2();
frm.show();
}
and I have a datagridview control inside this form which does a heavy loading from DB and loads data inside this datagridview in frm2.
the problem is that the main thread(form1 thread) is freezed at time when the data is attached to datagridview in frm2.Why is this happening.
I created two different projects and separate frm2 and form1 in these 2 projects and the results are ok and no freeze is happening to none of them.

Well, I think there is one single thread for any UI manipulation in WinForms.
If you are doing DB retrieval within Form constructor, then this is performed in the UI thread, and all forms freeze.
You should create a new thread to fetch your data from DB and set it later in the UI in the UI thread.

Related

How do I show a form only when data is loaded?

I need some help on a thing that I can't resolve.
I've a Windows Form with a DataGridView who is populated with a large amount of data.
This data is stored in a SQL Server Database and retrieved by a simple piece of ADO.NET code.
I'm already using the BackGroundWorker class to perform this kind of operation but the form still freezes for 1-2 second.
Is there a way to delay the showing of the form? Like a show form only when all data in loaded? I've tried to make it not visible or to use Hide() and Show() method but still not have any results.
I've found a easy work-around.
Change the modifiers of the BackGroundWorker to public.
Create the instance of the form, run Form.CreateControl() and call the BackGroundWorker.RunWorkerAsync() method.
In the DoWork event I put: data retrieving and population of the controls (DataGridView, combobox and textbox).
In the RunWorkerCompleted just the Form.Show() method.
That's all. Is this a nice solution?
Remove InitializeComponent(); from constructor of your form and call this method when data has loaded

Controls not loading when multithreading a form

I have a C# WinForms application. When the main form loads, two other forms are loaded and hidden as I need them to run in the background. I keep 2 global variables pointing to them in order to be able to access them. I want to load them on a separate thread to reduce the loading time. For this, I have the following code in the main form's constructor:
if (!IsHandleCreated)
{
CreateHandle();
}
//Start the license manager using a separate thread.
new Thread(delegate()
{
BeginInvoke(new Action(() =>
{
GlobalVariables.licenseManagerWindow = new LicenseManager();
GlobalVariables.licenseManagerWindow.Show();
}));
bt_Licenses.BeginInvoke(new Action(() =>
{
bt_Licenses.Enabled = true;
}));
}).Start();
//Start the employee app manager using a separate thread.
new Thread(delegate()
{
BeginInvoke(new Action(() =>
{
GlobalVariables.employeeAppManagerWindow = new ManageEmployeeApp();
GlobalVariables.employeeAppManagerWindow.Show();
}));
bt_Employees.BeginInvoke(new Action(() =>
{
bt_EmployeeApp.Enabled = true;
}));
}).Start();
The problem is that instead of showing the main form and updating the buttons after each form is loaded, the main form looks like this until both of the other forms are loaded:
As you can see, the controls don't load and they show transparently. Eventually they all load fine when the 2 forms load. If I comment out the code above, the main form loads perfectly fine. I obviously don't want the user to see this skeleton while the forms load, but to see the controls. What am I doing wrong?
Try using background workers instead of threads. use the report progress / runworkercomplete events to write the results to your form. This prevents your UI waiting with the draw until the threads are completed and makes it that you don't have to manually handle the invokes of controls.
documentation and examples can be found here: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker(v=vs.110).aspx
Instead of loading the two hidden forms in the main form's constructor, do it in the main form's "Load" event handler.
Windows forces you to load the hidden forms through the main thread (that's what BeginInvoke does), but you can minimize the impact to the main thread by having the hidden forms kick off the long-running network communication on a background thread from the hidden forms' "Load" event.

Multithreading multiple forms

I have one main form that starts on program in program.cs as Application.Run(new Form1());. It opens object of 2 different forms as:
Form2 obj1 = new Form2();
obj2.Show();
Form3 obj2 = new Form3();
obj3.Show();
Each form has Thread.Sleep(10000) code but the problem is that on one's Thread.Sleep execution, whole programs sleeps. I thought if one form's Thread.Sleep runs, it will not stop other form and will not affect those.
All GUI components run on a single GUI thread therefore if you sleep that thread everything running on that thread sleeps, which in your case means all of your forms.
If you need a particular form to wait for something to happen then there are various ways of doing that, however, you need to be a bit more clear in your question as to what exactly it is you are waiting for.

New form on a different thread

So I have a thread in my application, which purpose is to listen to messages from the server and act according to what it recieves.
I ran into a problem when I wanted to fire off a message from the server, that when the client app recieves it, the client app would open up a new form. However this new form just freezes instantly.
I think what's happening is that the new form is loaded up on the same thread as the thread listening to the server, which of course is busy listening on the stream, in turn blocking the thread.
Normally, for my other functions in the clients listening thread, I'd use invokes to update the UI of the main form, so I guess what I'm asking for is if here's a way to invoke a new form on the main form.
I assume this is Windows Forms and not WPF? From your background thread, you should not attempt to create any form, control, etc or manipulate them. This will only work from the main thread which has a message loop running and can process Windows messages.
So to get your code to execute on the main thread instead of the background thread, you can use the Control.BeginInvoke method like so:
private static Form MainForm; // set this to your main form
private void SomethingOnBackgroundThread() {
string someData = "some data";
MainForm.BeginInvoke((Action)delegate {
var form = new MyForm();
form.Text = someData;
form.Show();
});
}
The main thing to keep in mind is that if the background thread doesn't need any response from the main thread, you should use BeginInvoke, not Invoke. Otherwise you could get into a deadlock if the main thread is busy waiting on the background thread.
You basically gave the answer yourself - just execute the code to create the form on the GUI thread, using Invoke.

BackgroundWorker OnWorkCompleted throws cross-thread exception

I have a simple UserControl for database paging, that uses a controller to perform the actual DAL calls. I use a BackgroundWorker to perform the heavy lifting, and on the OnWorkCompleted event I re-enable some buttons, change a TextBox.Text property and raise an event for the parent form.
Form A holds my UserControl. When I click on some button that opens form B, even if I don't do anything "there" and just close it, and try to bring in the next page from my database, the OnWorkCompleted gets called on the worker thread (and not my Main thread), and throws a cross-thread exception.
At the moment I added a check for InvokeRequired at the handler there, but isn't the whole point of OnWorkCompleted is to be called on the Main thread? Why wouldn't it work as expected?
EDIT:
I have managed to narrow down the problem to arcgis and BackgroundWorker. I have the following solution wich adds a Command to arcmap, that opens a simple Form1 with two buttons.
The first button runs a BackgroundWorker that sleeps for 500ms and updates a counter.
In the RunWorkerCompleted method it checks for InvokeRequired, and updates the title to show whethever the method was originaly running inside the main thread or the worker thread.
The second button just opens Form2, which contains nothing.
At first, all the calls to RunWorkerCompletedare are made inside the main thread (As expected - thats the whold point of the RunWorkerComplete method, At least by what I understand from the MSDN on BackgroundWorker)
After opening and closing Form2, the RunWorkerCompleted is always being called on the worker thread. I want to add that I can just leave this solution to the problem as is (check for InvokeRequired in the RunWorkerCompleted method), but I want to understand why it is happening against my expectations. In my "real" code I'd like to always know that the RunWorkerCompleted method is being called on the main thread.
I managed to pin point the problem at the form.Show(); command in my BackgroundTesterBtn - if I use ShowDialog() instead, I get no problem (RunWorkerCompleted always runs on the main thread). I do need to use Show() in my ArcMap project, so that the user will not be bound to the form.
I also tried to reproduce the bug on a normal WinForms project. I added a simple project that just opens the first form without ArcMap, but in that case I couldn't reproduce the bug - the RunWorkerCompleted ran on the main thread, whether I used Show() or ShowDialog(), before and after opening Form2. I tried adding a third form to act as a main form before my Form1, but it didn't change the outcome.
Here is my simple sln (VS2005sp1) - it requires
ESRI.ArcGIS.ADF(9.2.4.1420)
ESRI.ArcGIS.ArcMapUI(9.2.3.1380)
ESRI.ArcGIS.SystemUI (9.2.3.1380)
Isn't the whole point of OnWorkCompleted is to be called on the Main thread? Why wouldn't it work as expected?
No, it's not.
You can't just go running any old thing on any old thread. Threads are not polite objects that you can simply say "run this, please".
A better mental model of a thread is a freight train. Once it's going, it's off on it's own track. You can't change it's course or stop it. If you want to influence it, you either have to wait til it gets to the next train station (eg: have it manually check for some events), or derail it (Thread.Abort and CrossThread exceptions have much the same consequences as derailing a train... beware!).
Winforms controls sort of support this behaviour (They have Control.BeginInvoke which lets you run any function on the UI thread), but that only works because they have a special hook into the windows UI message pump and write some special handlers. To go with the above analogy, their train checks in at the station and looks for new directions periodically, and you can use that facility to post it your own directions.
The BackgroundWorker is designed to be general purpose (it can't be tied to the windows GUI) so it can't use the windows Control.BeginInvoke features. It has to assume that your main thread is an unstoppable 'train' doing it's own thing, so the completed event has to run in the worker thread or not at all.
However, as you're using winforms, in your OnWorkCompleted handler, you can get the Window to execute another callback using the BeginInvoke functionality I mentioned above. Like this:
// Assume we're running in a windows forms button click so we have access to the
// form object in the "this" variable.
void OnButton_Click(object sender, EventArgs e )
var b = new BackgroundWorker();
b.DoWork += ... blah blah
// attach an anonymous function to the completed event.
// when this function fires in the worker thread, it will ask the form (this)
// to execute the WorkCompleteCallback on the UI thread.
// when the form has some spare time, it will run your function, and
// you can do all the stuff that you want
b.RunWorkerCompleted += (s, e) { this.BeginInvoke(WorkCompleteCallback); }
b.RunWorkerAsync(); // GO!
}
void WorkCompleteCallback()
{
Button.Enabled = false;
//other stuff that only works in the UI thread
}
Also, don't forget this:
Your RunWorkerCompleted event handler should always check the Error and Cancelled properties before accessing the Result property. If an exception was raised or if the operation was canceled, accessing the Result property raises an exception.
It looks like a bug:
http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=116930
http://thedatafarm.com/devlifeblog/archive/2005/12/21/39532.aspx
So I suggest using the bullet-proof (pseudocode):
if(control.InvokeRequired)
control.Invoke(Action);
else
Action()
The BackgroundWorker checks whether the delegate instance, points to a class which supports the interface ISynchronizeInvoke. Your DAL layer probably does not implement that interface. Normally, you would use the BackgroundWorker on a Form, which does support that interface.
In case you want to use the BackgroundWorker from the DAL layer and want to update the UI from there, you have three options:
you'd stay calling the Invoke method
implement the interface ISynchronizeInvoke on the DAL class, and redirect the calls manually (it's only three methods and a property)
before invoking the BackgroundWorker (so, on the UI thread), to call SynchronizationContext.Current and to save the content instance in an instance variable. The SynchronizationContext will then give you the Send method, which will exactly do what Invoke does.
The best approach to avoid issues with cross-threading in GUI is to use SynchronizationContext.

Categories