Reputation: 259
I have build application using VS 2010 , C#.
I am using BackgroundWorker
in my application.
While I click on button code getting records from database and showing into Datagrid. but the problem is while I running it from code it's working fine but when I running program .exe so it's being hang.
//Declared delegate
delegate void SetControlPropertyThreadSafeDelegate(Control control, string propertyName, object propertyValue);
//Declared method to run control Thread safe
public static void SetControlPropertyThreadSafe(Control control, string propertyName, object propertyValue)
{
if (control.InvokeRequired)
{
control.Invoke(new SetControlPropertyThreadSafeDelegate(SetControlPropertyThreadSafe), new object[] { control, propertyName, propertyValue });
}
else
{
control.GetType().InvokeMember(propertyName, BindingFlags.SetProperty, null, control, new object[] { propertyValue });
}
}
//calling method like below
SetControlPropertyThreadSafe(dataGridView1, "DataSource", dtGrid2);
I don't understand where I done mistake. why the program is hanging ?
Upvotes: 2
Views: 1592
Reputation: 5650
It sounds like you're using BackgroundWorker
... wrong.
The BackgroundWorker
class allows you to perform an operation in the background, provide information to the UI about progress and finally return a complete result.
As the OP states:
delegate is required to call Thread safe.
SetControlPropertyThreadSafe(dataGridView1, "DataSource", dtGrid2);
is running under Backgroundworker_DoWork() .. while my application running I need to show the output on Main window.
You're not supposed to update the UI from the DoWork
method manually. If you must update the UI in any fashion, you can use the ProgressChanged
event available on the BackgroundWorker
class and call ReportProgress
during the execution of DoWork
. Also make sure to set WorkerReportsProgress
to true.
The ReportProgress
method is flexible enough to allow you to pass almost anything back to the UI if you must (the ProgressChangedEventArgs
class contains a UserState
object property which you can use to pass any kind of data you want). Check the example code given on BackgroundWorker
MSND page.
Here's my example:
System.ComponentModel.BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();
void StartBackgroundTask()
{
worker.DoWork += worker_DoWork;
//if it's possible to display progress, use this
worker.WorkerReportsProgress = true;
worker.ProgressChanged += worker_ProgressChanged;
//what to do when the method finishes?
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
//start!
worker.RunWorkerAsync();
}
void worker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
//perform any "finalization" operations, like re-enable disabled buttons
//display the result using the data in e.Result
//this code will be running in the UI thread
}
//example of a container class to pass more data in the ReportProgress event
public class ProgressData
{
public string OperationDescription { get; set; }
public int CurrentResult { get; set; }
//feel free to add more stuff here
}
void worker_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
//display the progress using e.ProgressPercentage or e.UserState
//this code will be running in the UI thread
//UserState can be ANYTHING:
//var data = (ProgressData)e.UserState;
}
void worker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
//this code will NOT be running in the UI thread!
//you should NOT call the UI thread from this method
int result = 1;
//perform calculations
for (var i = 1; i <= 10; i++)
{
worker.ReportProgress(i, new ProgressData(){ OperationDescription = "CustomState passed as second, optional parameter", CurrentResult = result });
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
result *= i;
}
e.Result = result;
}
Unless you must display the already loaded elements to the user while still loading them, then you should use ReportProgress
to display just that - progress. Use the RunWorkerCompleted
event to finally pass the results to the UI.
If you use your own UI-updating delegates, then you might as well ditch BackgroundWorker
altogether and use a Task
instead.
Upvotes: 4
Reputation: 942257
void SetControlPropertyThreadSafe(...)
There's a Big Problem with methods like this. Unfortunately very hard to root out, there are way too many posts on SO that recommend this. The problem is that it lulls a programmer to sleep, it is "safe" so surely it is not the cause of the problem. Turning a problem into an undebuggable issue.
There is absolutely nothing "safe" about it:
Using Control.Invoke() is dangerous, it is very prone to cause deadlock. Triggered when there's code in the UI thread that does something unwise like waiting for the worker thread to complete. Only use Invoke() when you have your back to the wall, actually requiring the return value. And when you find out that's necessary then don't do it, it always races unless you disable the UI. Always use BeginInvoke() instead.
It hides a fire-hose problem. You'll be lulled into calling the method to update every single control and not thinking about the problem that this causes. Which is pummeling the UI thread with the invoke requests. Do this at a rate ~50 times higher than the human eye can see and you'll bury the UI thread. It can never catch up with the invoke requests, as soon as it dispatched one then there's another one waiting to be executed. The UI thread now stops taking care of its normal duties, like painting windows and handling user input. It looks frozen, just like it would if you ran this code directly
Very unpleasant things happen when the user closes the window but your worker thread keeps motoring. Invoking to a window that no longer exists. This usually gives a loud bang so isn't too hard to diagnose. Sometimes it doesn't, impossible to debug, there is an inevitable threading race between stopping the thread and allowing the window to close, one that can only be solved by not closing the window but hiding it instead.
Your problem is the second bullet. It hangs because your code is running faster now. The Invoke() call hid the problem in the debugger. Get rid of this code completely, it is dangerous, and gather results from the dbase query in, say, a List<>. Pass it to the ReportProgress() method, once in a while so you don't hose the UI thread. Re-create the List after the call so it is thread-safe.
Upvotes: 5