I am wondering if the following will affect the performance of my application.
Every call I make to update the background worker I do the right way without cross threading, but if I request a value from the UserInterface and not update it, is this illegal and could it affect the performance?
Here is a small snippet from my code.
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
Settings settings = e.Argument as Settings;
BackgroundWorker worker = sender as BackgroundWorker;
if ((worker.CancellationPending))
{
e.Cancel = true;
}
else
{
if (settings.O1_Enabled)
{
try
{
counter_O1 = Convert.ToInt32(this.rowcount_O1.Text);
Any advice would be greatly appreciated.
No, It is not legal.
You can't access UI in background worker like this. To do so You need to invoke this, for example:
Dispatcher.Invoke(new Action(() =>
{
counter_O1 = Convert.ToInt32(this.rowcount_O1.Text);
}));
Related
I have a C# WinForms application with a tab control and several tabs. One of the tabs contains a data grid control - it only has about 10 elements in it but the data is populated by querying multiple servers and thus is slow to load.
When I run my application and select the tab with the datagrid control, the application appears to hang, while its trying to query all the servers and populate the grid.
Instead of hanging I'd like the application to be responsive and for it to display a "please wait..." message which will disappear after the datagrid is populated.
What I've tried to do is create a background worker as such:
if (tabctrl.SelectedTab == tabctrl.TabPages["tabServices"])
{
this.dgrdServices.RowPrePaint += new DataGridViewRowPrePaintEventHandler(dgrdServices_RowPrePaint);
this.dgrdServices.CellContentClick += new DataGridViewCellEventHandler(dgrdServices_CellClick);
BackgroundWorker bw = new BackgroundWorker();
lblLoading.Visible = true;
bw.RunWorkerAsync();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
PopulateServicesDataGrid();
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
lblLoading.Visible = false;
}
private void PopulateServicesDataGrid()
{
int x = 0;
foreach (Service Service in Globals.Services)
{
// Add a row to the datagrid for each service
this.dgrdServices.Rows.Add();
// Update the current service status
Service.Status = Service.Query(Service.Server, Service.Name);
if (Service.Status == "running")
{
this.dgrdServices.Rows[x].Cells[0].Value = Properties.Resources.green_dot;
this.dgrdServices.Rows[x].Cells[4].Value = Properties.Resources.stop_enabled;
}
else
{
this.dgrdServices.Rows[x].Cells[0].Value = Properties.Resources.grey_dot;
this.dgrdServices.Rows[x].Cells[4].Value = Properties.Resources.start_enabled;
}
this.dgrdServices.Rows[x].Cells[1].Value = Service.Server.ToUpper();
this.dgrdServices.Rows[x].Cells[2].Value = Service.FreindlyName;
this.dgrdServices.Rows[x].Cells[3].Value = Service.Status;
this.dgrdServices.Rows[x].Cells[5].Value = "Uninstall";
this.dgrdServices.Rows[x].Cells[6].Value = Service.Name;
x++;
}
}
PopulateServicesDataGrid() contains code which iterates through some objects and queries several different servers for service status.
When I try and run the above though the grid doesn't get populated. If I don't use a background worker and just call PopulateServicesDataGrid directly it does work (albeit the app hangs).
Why isn't the background worker/datagrid populate working?
In your PopulateServicesDataGrid I imagine you're interacting with a UI control, which doesn't work out because the background worker is operating on a different thread than your UI context. You'll need to work out a mechanism to do the work in a way that returns the information you want to put in the grid and then back in your UI thread context (RunWorkerCompleted), populate the grid with the information you come up with in DoWork.
Anytime you're using a background worker, you'll need to split out your interactions with the UI controls, and after the backgroundworker completes resume interaction with your UI.
You're also hooking up the events after calling RunWorkerAsync, hook up your events first then call RunWorkerAsync.
Edit to reflect comment with an example:
Rough example of how you could do this, based on the code I see.
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
QueryServices()
}
private void QueryServices()
{
foreach (Service Service in Globals.Services)
{
Service.Status = Service.Query(Service.Server, Service.Name);
}
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
PopulateServicesDataGrid();
lblLoading.Visible = false;
}
private void PopulateServicesDataGrid()
{
//Do everything else you are doing originally in this method minus the Service.Query calls.
}
Method bw_DoWork running in another thread from ThreadPool. Accessing WinForms object from other threads requires synchronization. The best way to do this - use AsyncOperationManager. You should create AsyncOperation in GUI thread and use it inside PopulateServicesDataGrid to send or post results.
Another way - update DataGrid by prepared data inside bw_RunWorkerComplete - it's already synchronized by BackgroundWorker component.
More modern way to do the same - use async tasks, but it requires base level of TPL knowledge.
I'm just trying to run a new thread each time a button click even occurs which should create a new form. I tried this in the button click event in the MainForm:
private void button1_Click(object sender, EventArgs e)
{
worker1 = new Thread(new ThreadStart(thread1));
worker2 = new Thread(new ThreadStart(thread2));
worker1.Start();
worker2.Start();
}
private void thread1()
{
SubForm s = new SubForm();
s.Show();
}
private void thread2()
{
SubForm s = new SubForm();
s.Show();
}
The code in the Subform button click event goes like this:
private void button1_Click(object sender, EventArgs e)
{
int max;
try
{
max = Convert.ToInt32(textBox1.Text);
}
catch
{
MessageBox.Show("Enter numbers", "ERROR");
return;
}
progressBar1.Maximum = max;
for ( long i = 0; i < max; i++)
{
progressBar1.Value = Convert.ToInt32(i);
}
}
Is this the right way? Because I'm trying to open two independent forms, operations in one thread should not affect the other thread.
Or is BackGroundworker the solution to implement this? If yes, can anyone please help me with that?
You do not need to run forms in separate threads. You can just call s.Show() on multiple forms normally. They will not block each other.
Of course, if you’re doing something else, like some sort of calculation or other task that takes a long while, then you should run that in a separate thread, but not the form.
Here is a bit of code that will let you create a progress bar that shows progress for a long process. Notice that every time to access the form from inside the thread, you have to use .Invoke(), which actually schedules that invocation to run on the GUI thread when it’s ready.
public void StartLongProcess()
{
// Create and show the form with the progress bar
var progressForm = new Subform();
progressForm.Show();
bool interrupt = false;
// Run the calculation in a separate thread
var thread = new Thread(() =>
{
// Do some calculation, presumably in some sort of loop...
while ( ... )
{
// Every time you want to update the progress bar:
progressForm.Invoke(new Action(
() => { progressForm.ProgressBar.Value = ...; }));
// If you’re ready to cancel the calculation:
if (interrupt)
break;
}
// The calculation is finished — close the progress form
progressForm.Invoke(new Action(() => { progressForm.Close(); }));
});
thread.Start();
// Allow the user to cancel the calculation with a Cancel button
progressForm.CancelButton.Click += (s, e) => { interrupt = true; };
}
Although I'm not 100% aware of anything that says running completely seperate forms doing completely isolated operations in their own threads is dangerous in any way, running all UI operations on a single thread is generally regarded as good practice.
You can support this simply by having your Subform class use BackgroundWorker. When the form is shown, kick off the BackgroundWorker so that it processes whatever you need it to.
Then you can simply create new instances of your Subform on your GUI thread and show them. The form will show and start its operation on another thread.
This way the UI will be running on the GUI thread, but the operations the forms are running will be running on ThreadPool threads.
Update
Here's an example of what your background worker handlers might look like - note that (as usual) this is just off the top of my head, but I think you can get your head around the basic principles.
Add a BackgroundWorker to your form named worker. Hook it up to the following event handlers:
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Executed on GUI thread.
if (e.Error != null)
{
// Background thread errored - report it in a messagebox.
MessageBox.Show(e.Error.ToString());
return;
}
// Worker succeeded.
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Executed on GUI thread.
progressBar1.Value = e.ProgressPercentage;
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
// Executed on ThreadPool thread.
int max = (int)e.Argument;
for (long i = 0; i < max; i++)
{
worker.ReportProgress(Convert.ToInt32(i));
}
}
Your click handler would look something like:
void button1_Click(object sender, EventArgs e)
{
int max;
try
{
// This is what you have in your click handler,
// Int32.TryParse is a much better alternative.
max = Convert.ToInt32(textBox1.Text);
}
catch
{
MessageBox.Show("Enter numbers", "ERROR");
return;
}
progressBar1.Maximum = max;
worker.RunWorkerAsync(max);
}
I hope that helps.
Try this. It runs the new Form on its own thread with its own message queues and what not.
Run this code:
new Thread(new ThreadStart(delegate
{
Application.Run(new Form());
})).Start();
Use Thread.CurrentThread.GetHashCode() to test that is runs on different thread.
It's possible to run different forms on different threads. There are two caveats I'm aware of:
Neither form may be an MDI client of the other. Attempting to make a form an MDI client of another when the forms have different threads will fail.
If an object will be sending events to multiple forms and all forms use the same thread, it's possible to synchronize the events to the main thread before raising it. Otherwise, the event must be raised asynchronously and each form must perform its own synchronization mechanism for incoming events.
Obviously it's desirable not to have any window's UI thread get blocked, but using separate threads for separate windows may be a nice alternative.
I'm very new to multithreading and lack experience. I need to compute some data in a different thread so the UI doesn't hang up, and then send the data as it is processed to a table on the main form. So, basically, the user can work with the data that is already computed, while other data is still being processed. What is the best way to achieve this? I would also be very grateful for any examples. Thanks in advance.
If you don't want to use the Background worker as answered by KMan you can create a thread yourself.
private void startJob(object work) {
Thread t = new Thread(
new System.Threading.ParameterizedThreadStart(methodToCall)
);
t.IsBackground = true; // if you set this, it will exit if all main threads exit.
t.Start(work); // this launches the methodToCall in its own thread.
}
private void methodToCall(object work) {
// do the stuff you want to do
updateGUI(result);
}
private void updateGUI(object result) {
if (InvokeRequired) {
// C# doesn't like cross thread GUI operations, so queue it to the GUI thread
Invoke(new Action<object>(updateGUI), result);
return;
}
// now we are "back" in the GUI operational thread.
// update any controls you like.
}
Checkout this BackgroundWorker sample document.
Initialise your background worker object
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
// I need to compute some data in a different thread so the UI doesn't hang up
// Well! ompute some data here.
bw.ReportProgress(percentOfCompletion, yourData) // and then send the data as it is processed
// percentOfCompletion-int, yourData-object(ie, you can send anything. it will be boxed)
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// to a table on the main form. So, basically, the user can work with the data that is already computed, while other data is still being processed
List<string> yourData = e.UserState as List<string>; // just for eg i've used a List.
}
What is the best way to achieve this?
RunWorkerAsync(); //This will trigger the DoWork() method
Use registry key to share the data between the threads
You can send the data to static variable, static variables are shared across threads.
I have method which create background thread to make some action. In this background thread I create object. But this object while creating in runtime give me an exception :
The calling thread must be STA, because many UI components require this.
I know that I must use Dispatcher to make reflect something to UI. But in this case I just create an object and dont iteract with UI. This is my code:
public void SomeMethod()
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(Background_Method);
worker.RunWorkerAsync();
}
void Background_Method(object sender, DoWorkEventArgs e)
{
TreeView tv = new TreeView();
}
How can I create objects in background thread?
I use WPF application
TreeView is a UI control. You can only create and manipulate UI controls on a UI thread, so what you're trying to do is not possible.
What you want to do is do all of the time-consuming work on the background thread, and then "call back" to the UI thread to manipulate the UI. This is actually quite easy:
void Background_Method(object sender, DoWorkEventArgs e)
{
// ... time consuming stuff...
// call back to the window to do the UI-manipulation
this.BeginInvoke(new MethodInvoker(delegate {
TreeView tv = new TreeView();
// etc, manipulate
}));
}
I may have got the syntax wrong for BeginInvoke (it's off the top of my head), but there you go anyway...
HTH:
void Background_Method(object sender, DoWorkEventArgs e)
{
// Time Consuming operations without using UI elements
// Result of timeconsuming operations
var result = new object();
App.Current.Dispatcher.Invoke(new Action<object>((res) =>
{
// Working with UI
TreeView tv = new TreeView();
}), result);
}
No one is discussing the case of a separate STA thread in details (even though the concept is exactly the same).
So let's imagine a simple tab control added at a button click
private void button_Click(object sender, RoutedEventArgs e)
{
TabItem newTab = new TabItem() { Header = "New Tab" };
tabMain.Items.Add(newTab);
}
If we move it to another STA thread
private void button_Click(object sender, RoutedEventArgs e)
{
Thread newThread = new Thread(new ThreadStart(ThreadStartingPoint));
newThread.SetApartmentState(ApartmentState.STA);
newThread.IsBackground = true;
newThread.Start();
}
private void ThreadStartingPoint()
{
TabItem newTab = new TabItem() { Header = "New Tab" };
tabMain.Items.Add(newTab);
}
of course we get a System.InvalidOperationException
Now, what happens if we add the control
private void AddToParent(string header)
{
TabItem newTab = new TabItem() { Header = header };
tabMain.Items.Add(newTab);
}
using a delegate method?
public void DelegateMethod(string header)
{
tabMain.Dispatcher.BeginInvoke(
new Action(() => {
this.AddToParent(header);
}), null);
}
it does work if you call it
private void button_Click(object sender, RoutedEventArgs e)
{
Thread newThread = new Thread(new ThreadStart(ThreadStartingPoint));
newThread.SetApartmentState(ApartmentState.STA);
newThread.IsBackground = true;
newThread.Start();
}
private void ThreadStartingPoint()
{
DelegateMethod("new tab");
}
because of course now we keep the visual tree in the same original thread.
To make your code simply work, you must join a STA COM apartment by calling Thread.SetApartmentState(ApartmentState.STA). Since BackgroundWorker is probably using some shared thread pool, joining a particular apartment may affect other users of this thread pool or may even fail if it has already been set to e.g. MTA before. Even if it all worked out, your newly created TreeView would be locked to this worker thread. You wouldn't be able to use it in your main UI thread.
If you explained in a bit more detail about your true intentions, you would surely get better help.
Try following Code:
public void SomeMethod()
{
System.ComponentModel.BackgroundWorker myWorker = new System.ComponentModel.BackgroundWorker();
myWorker.DoWork += myWorker_DoWork;
myWorker.RunWorkerAsync();
}
private void myWorker_DoWork(object sender,
System.ComponentModel.DoWorkEventArgs e)
{
// Do time-consuming work here
}
void Background_Method(object sender, DoWorkEventArgs e)
{
TreeView tv = new TreeView();
// Generate your TreeView here
UIDispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
someContainer.Children.Add(tv);
};
}
I solved my problem. I just used e.Result property of RunWorkerCompleted method. I get data in background thread and then use this data when thread completed. Thank every body for useful methods. Special thank to Veer to give a recommendation about e.Result property.
See the answer on this question:
How to run something in the STA thread?
When you define your thread, set the ApartmentState to STA:
thread.SetApartmentState(ApartmentState.STA);
This should do the trick!
I am having fun with WPF and got a problem. I have googled and found this website that has the same problem of me but without any working solution.
The problem is that I have a button that do some processing of data (around 30 sec). I want to have the button to disable and to have log writing in a text box... the problem is that it doesn't disable and it doesn't wrote any thing on the textbox until the processing is completely done.
Any idea?
private void button1_Click(object sender, RoutedEventArgs e)
{
this.button1.IsEnabled = false;
//Long stuff here
txtLog.AppendText(Environment.NewLine + "Blabla");
//End long stuff here
this.button1.IsEnabled = true;
}
As others have said, use the BackgroundWorker or some other method of doing work asychronously.
You can declare it under your Window, initialize it somewhere like the Loaded event, and use it in the Click event. Here's your method, modified to use BackgroundWorker, assuming you've declared it under the Window as _bw:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_bw = new BackgroundWorker();
_bw.DoWork += new DoWorkEventHandler((o, args) =>
{
//Long stuff here
this.Dispatcher.Invoke((Action)(() => txtLog.AppendText(Environment.NewLine + "Blabla")));
});
_bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler((o, args) =>
{
//End long stuff here
this.Dispatcher.Invoke((Action)(() => this.button1.IsEnabled = true));
});
}
private void button1_Click(object sender, RoutedEventArgs e)
{
this.button1.IsEnabled = false;
_bw.RunWorkerAsync();
}
Note that anything that modifies your UI from another thread must be done within a Dispatcher.Invoke or Dispatcher.BeginInvoke call, WPF does not allow you to get or set DependencyProperty values from any thread but the one where the object was created (more about this here).
If you wanted to read from txtLog instead of modifying it, the code would be the same:
//Long stuff here
this.Dispatcher.Invoke((Action)(() =>
{
string myLogText = txtLog.Text;
myLogText = myLogText + Environment.NewLine + "Blabla";
txtLog.Text = myLogText;
}));
That operation is being performed on the UI thread. This means that it will block the Windows message pump from processing until it has completed. no pump = no UI updates. You should launch the job on another thread. I don't know WPF, but in C# I would use either the Thread or BackgroundWorker classes.
do it async. create a backgroundworker process to handle the data and the application will continue to respond. MSDN Resources on the Class. Since WPF is using C# (or VB.net) you can still use the same types of threading objects. I've used the background worker successfully in a WPF app myself.