I need to generate n random strings and this process may take a while and block the main thread UI. For avoid this and let user use the programm while the process is running I decided to use a backGroundWorker
. But it didn't work well and the main thread still is blocked. In my DoWork
event I have something like this:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// using Invoke because I change value of some controls, e.g, ListView, labels and a progressbar
this.Invoke((Action)delegate
{
for (int i = 0; i < nSteps; ++i)
{
string s = getnStr();
// ...
int progress = (int)(100.0 / nSteps * (i + 1));
backgroundWorker1.ReportProgress(progress);
}
});
}
Even although I call ReportProgress()
inside the loop, the progressbar only changes its value when the loop is done.
Why is this happening and how can I fix this?
Invoke... That's where you get it all wrong. You're only creating a background worker just so you invoke the code back to the main thread (that's what Invoke does).
You should actually either use invoke only for the controls access, either modify the controls only in the ReportProgress handler and then make sure you call the ReportProgress method every time when needed.
EDIT: Clarification: your problem with the invoke is that you're invoking the UI thread for the entire workload that the background worker was supposed to do.
The other answer explained the behavior you are seeing. Please read it first. Here is how to fix this:
BackgroundWorker
is obsolete because we have Task
and await
now. It automates a lot. Your code probably should look like this:
//In the button click handler:
for (int i = 0; i < nSteps; ++i)
{
await DoSomeWorkAsync();
int progress = (int)(100.0 / nSteps * (i + 1));
SetProgressBarValue(progress);
}
That's really all. You need to ensure that DoSomeWorkAsync
does not block. It must return a Task
.
Besides reporting progress ReportProgress() method can be used as a generic asynchronous event dispatcher (e.g to update text in UI controls):
for (int i = 0; i < nSteps; ++i)
{
string s = getnStr();
// Update text
backgroundWorker1.ReportProgress(0, "My text..");
// Update progress
int progress = (int)(100.0 / nSteps * (i + 1));
backgroundWorker1.ReportProgress(progress);
}
ProgressChanged
event handler would look something like this:
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.UserState != null)
{
// Handle text update
label1.Text = (string)e.UserState;
return;
}
progressBar1.Value = e.ProgressPercentage;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With