Why main thread gets hanged when background worker is executing - c#

I have simple winforms application where i have button. on button click i am doing something like this
private void Button1_Click(object sender, EventArgs e)
{
BackgroundWorker bw_Convert = new BackgroundWorker();
bw_Convert.WorkerReportsProgress = true;
bw_Convert.DoWork += bw_Convert_DoWork;
bw_Convert.RunWorkerCompleted += bw_Convert_RunWorkerCompleted;
bw_Convert.ProgressChanged += bw_Convert_ProgressChanged;
bw_Convert.WorkerReportsProgress = true;
bw_Convert.RunWorkerAsync();
}
I have following code for background worker
public void bw_Convert_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i <= 1000000; i++)
{
bw_Convert.ReportProgress((100 * (i) / 1000));
}
}
public void bw_Convert_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBarControl1.EditValue = e.ProgressPercentage.ToString();
}
By doing this. Why my Main Thread gets hanged as i m processing the things in Background Worker.

Because ReportProgress event handler is executed on main thread:
The call to the ReportProgress method is asynchronous and returns
immediately. The ProgressChanged event handler executes on the thread
that created the BackgroundWorker.
Thus you are constantly calling ReportProgress from background thread, it makes your main thread constantly busy handling this event.
If you have a lot of quick work to do in background, then try to report only each n-th iteration instead of raising event on each iteration:
public void bw_Convert_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i <= 1000000; i++)
{
if (i % 10000 == 0)
bw_Convert.ReportProgress(i);
// do something
}
}

The ProgressChanged event is executed on the UI thread so you can access the controls.
The code inside your background worker is executing in a very short amount of time, leading to many events being raised in a short period of time, making the code react as if it would execute completely on the UI thread.
In fact, most of the work is being done in the UI thread, because updating a control on the screen is much more work than the loop iterations.

Related

Progress Bar with Label — Unable to Update Label in ProgressChanged

I have a background worker with a long running task. The task goes through a list of files and I want to update the user with which file we are on. I have a tool strip that has a label named panel1.text. The progress bar is working however the label is not changing in my ProgressChanged method i.e. It should say Processing File1 then change to Processing File2, but it stays on the default of Processing.
private void btnProcess_Click(object sender, EventArgs e)
{
toolStripProgressBar1.Visible = true;
toolStripProgressBar1.Maximum = 1000000000;
panel1.Text = "Processing "; // this appears properly
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(processFiles);
worker.ProgressChanged += ProgressChanged;
worker.RunWorkerAsync();
while (worker.IsBusy)
{
// the reason for this is because nothing can happen until the processing is done
toolStripProgressBar1.Increment(1);
}
// more processing
}
private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
panel1.Text = "Processing "+ e.UserState.ToString(); <<<---- This is Not Updating panel1.Text but it evaluates properly
}
private void processFiles(object sender, EventArgs e)
{
int retVal = 0;
foreach (string fileName in listBox1.Items)
{
ProgressChangedEventArgs ea = new ProgressChangedEventArgs(1,fileName);
ProgressChanged(this, ea);
// do more processing
}
}
I would appreciate any help.
You are using the same thread, which is being blocked by another process. You need to use a Task to create a new thread and possibly use Dispatcher.BeginIvoke if the control is on the other thread. Make sure whatever Button Click, etc is happening is marked with the Async keyword as well to make it Asynchronous.
Example:
Await Task mytask = Task.Run(() =>
for(var i = 0; i < 1000; i++)
{
Label.Dispatcher.BeginInvoke( () =>
UpdateMe(int i, LabelClass/Component class/component)});
Then inside the Label Class or wherever the label is:
Public void UpdateMe(int i, LabelClass class)
{
class.label.content = Cint((i/Total)*100);
Thread.Sleep(500);
}
There are other ways to do it as well such as Binding the value to the UI, but this will give you a better understanding of why its not working and how things work with other threads.
If you want to really get a visual understanding call:
`Console.WriteLine($"Current Thread ID: System.Threading.Thread.CurrentThread.ManagedThreadId}");`
Right before you go into the Task---it will give you the main thread ID
Then inside the Task call it again...this will give you the secondary thread ID.
Then Right before the Dispatcher call:
Console.WriteLine($"Do I have access to the label on this thread? {Label.Dispatcher.CheckAccess()}";
If you have access it will display True, if not it will display False...In your case it will display false because its owned by the other thread, but you can use the Dispatcher to be able to do work on that thread while in another thread...
Also, I recommend you not use Background Worker and use Tasks instead...this explains why in depth...basically Tasks do everything Background workers do and more, have less issues and are easier to work with...
http://blog.stephencleary.com/2013/09/taskrun-vs-backgroundworker-conclusion.html
As already commented by Ivan, remove the while loop while (worker.IsBusy) as it's blocking the UI thread to process further. As well, you should enable the WorkerReportsProgress to true
worker.WorkerReportsProgress = true;
worker.ProgressChanged += ProgressChanged;
while (!worker.IsBusy)
{
worker.RunWorkerAsync();
}
Per your comment, move those later processing to BackgroundWorker.RunWorkerCompleted Event

BackgroundWorker not exiting loop

I have an BackgroundWorker:
BackgroundWorker worker;
private void Form1_Load(object sender, EventArgs e)
{
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.WorkerSupportsCancellation = true;
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.ProgressChanged +=
new ProgressChangedEventHandler(worker_ProgressChanged);
worker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
}
DoWork Event
void worker_DoWork(object sender, DoWorkEventArgs e)
{
int percentFinished = (int)e.Argument;
while (!worker.CancellationPending && percentFinished < 100)
{
percentFinished++;
worker.ReportProgress(percentFinished);
//here I start my operation
//operation....
//operation end
}
e.Result = percentFinished;
}
Progresschanged
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
Completed
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Asynchroner Thread kam bis zum Wert:
"+e.Result.ToString());
btnStartEnd.Text = "Starten";
}
And finally my button:
private void btnStartEnd_Click(object sender, EventArgs e)
{
if (worker.IsBusy)
{
worker.CancelAsync();
btnStartEnd.Text = "Starten";
}
else
{
if (progressBar1.Value == progressBar1.Maximum)
{
progressBar1.Value = progressBar1.Minimum;
}
worker.RunWorkerAsync(progressBar1.Value);
btnStartEnd.Text = "Stoppen";
}
}
This code works but I get a loop for my operations until the percentage is 100, so the operation starts 100 times and so takes 100 times longer.
The goal should be that the operation only starts one time and the percentage counts from 1-100.
Maybe I understand something wrong, but how does the worker know how far the operation is done? That value should be send to the progress bar for visualisation.
Normally you won’t add the loop inside the DoWork method
If you want to load for example 100 files from the file system and report the progress it could look like that:
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for(int i = 0; i < 100; i++)
{
// Load file and do something with the content
...
// Report the progress which causes the ProgressChanged event to be fired
// And update progressbar with the UI thread
worker.ReportProgress(i);
}
}
If you only have one long running operation that needs to be executed inside the DoWork method it needs to be executed asynchronously
Here is one example how you could call an action asynchronously in .NET:
Action action = () =>
{
for(int i = 0; i <100; i++)
{
Console.WriteLine(String.Format("Step {0} of long running operation", i));
Thread.Sleep(1000);
}
};
var r = action.BeginInvoke(null, null);
while(!r.IsCompleted)
{
Console.WriteLine("Waiting...");
Thread.Sleep(100);
}
However in .NET there are many more ways to do it. See for example:
http://msdn.microsoft.com/en-us/library/jj152938(v=vs.110).aspx for
Async patterns in .NET
http://msdn.microsoft.com/de-de/library/hh191443.aspx for Async
programming with await/async (new in .NET 4.5)
Maybe I understand something wrong, but how does the worker know how far the operation is done? That value should be send to the progress bar for visualisation.
The BackgroundWorker class doesn't know anything about your operations and about its progress. It's your job to determine when to report the progress.
In general the workflow for a background worker looks like this:
UI thread calls RunWorkerAsync.
DoWork event handler is called on a different thread. During the event handler you can report progress using the ReportProgress method
If you report a progress then the ProgressChanged event handler is called on the UI thread. Here you can update a progress bar for example.
When your event handler for the DoWork event exits the RunWorkerComplete event is raised.
Now why does every example for the BackgroundWorker has a for-loop? Because it's very easy to write, and measuring progress for a for-loop is trivial. Unfortunately this quite often isn't useful for different kind of operations.
If your long running operation processes N files then it's pretty obvious that you can update the progress bar after every item by 1/N. That's what the for-loop example does.
But if you only have one long running operation then you simply don't have any chance to get the progress unless the operation itself supports reporting it or if you can somehow estimate the progress.
The BackgroundWorker can't magically give a long running operation a progress bar. It only enables you to run the operation in the background.

.Net BackgroundWorker not responding

I am creating a BackgroundWorker in my C# application. Inside DoWork, few web services is called. Everything is working fine but as soon as the DoWork process is completed, the application is non-responsive. And also, I am not updating any UI component from BackgroundWorker.
Here is the code being executed in DoWork
private void bwNQUpdate_DoWork(object sender, DoWorkEventArgs e)
{
int nStatusCode = 0;
QDetails[] pQDetails = null;
string queueName = "test_queue";
int nQLength = cMezeoFileCloud.NQGetLength(ServiceUrl, queueName, ref nStatusCode);
if (nQLength > 0)
pQDetails = cFileCloud.GetData(ServiceUrl, queueName, nQLength, ref nStatusCode);
if (pQDetails != null)
{
for (int n = 0; n < pQDetails[0].nTotalQ; n++)
{
UpdateFromNQ(pNQDetails[n]);
cFileCloud.QValue(ServiceUrl , queueName, 1, ref nStatusCode);
}
}
}
And on RunWorkerCompleted
private void bwNQUpdate_RunWorkerCompleted(object sender, ProgressChangedEventArgs e)
{
MessageBox.Show("Download completed.");
}
After execution of RunWorkerCompleted, application is not responding.
First, check to see if MessageBox.Show("Download completed."); gets called by using a break point.
If it does get called, try invoking the message box call instead of calling it directly. Basically, any events fired from within the background worker may be coming from its under lying thread.
RunWorkerCompleted you have written is perfectly fine.
RunWorkerCompleted event is handled by the invoking thread, not the worker thread.
Even if you do MessageBox.Show from background thread there will not be a problem as the thread will make its own Uicomponent.
The problem i doubt is within your DoWork.

progressBar separate thread

I have question about progressbar show value.
I have this main thread
private void button1_Click(object sender, EventArgs e)
{
progress prog = new progress();
progress.progressEvent += new progress.progressEventHandler(progressEvent);
for(int i=0;i<100;i++)
{
Thread.Sleep(100);
prog.incA();
}
}
void progressEvent(object sender)
{
if (progressBar1.InvokeRequired)
{
//Tady mi to caka az kym nedobehne cyklus for a pak zacne tohleto fungovat
progressBar1.Invoke(new ChangeProgressBarValue(ProgressStep));
}
else
{
ProgressStep();
}
}
public void ProgressStep()
{
progressBar1.PerformStep();
}
public class progress
{
private ThreadStart ts;
private Thread th;
private bool status = true;
public delegate void progressEventHandler(object sender);
public static event progressEventHandler progressEvent;
private int b,a = 0;
public progress()
{
ts=new ThreadStart(go);
th = new Thread(ts);
th.IsBackground = true;
th.Start();
}
public void incA()
{
a++;
if(a==100)
status = false;
}
private void go()
{
while (status)
{
if (a != b)
{
b = a;
if (progressEvent != null)
progressEvent(this);
}
}
th.Abort();
}
}
and my problem is IF start main thread and call IncA this method call event and in event is progressbar invoke
and this invoke waiting to end main thread FOR
why waiting?
thx
Your loop in the main thread is preventing "paint" events from happening. Since you are calling your progress bar's function from withing that thread, you will never see the updates.
You need to move the code to do the incrementing to another thread entirely.
Here is a sample of what you want to do using a Button, a BackgroundWorker, and a ProgressBar:
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
backgroundWorker1.ReportProgress(i);
Thread.Sleep(100);
}
}
private void backgroundWorker1_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
}
Hope this helps!
The progress bar control is a UI object, and is created on the UI thread. When you call Invoke or BeginInvoke to update it, you are asking the UI thread to do the updating.
However, the UI thread is busy - in your button CLick event handler, you have a loop which Sleep()s the thread and calls prog.IncA in a loop. So it never exits back to the main UI loop (which is what dispatches windows messages and updates the UI). Your progress bar is being updated internally, but it never gets a chance to redraw because the UI thread is "busy".
The "processing" code (that is looping and calling prog.IncA()) should not be running on the UI thread at all - you need to start it off in a separate thread and then exit your Click handler so that the UI can continue to update.
Note that this has a side effect - if your UI thread is running, then the user will be able to continue interacting with your program, and so they can click again on the button and kick off another background thread - so you have to be very careful to make sure that the user can't do anything "dangerous" in the UI while you are busy processing.
I suggest you look at some introduction-to-threading tutorials to get an idea of how to use BackgroundWorker or another mechanism for running code in a separate thread. Once you understand that, you can add a progress bar. (And note that although a progress bar sounds like the simplest thing to do, it is actually rather a difficult thing to do due to the need for the UI thread to continue running but not let the user do anything dangerous during your processing)

Restart background worker

Is there a way to directly "restart" a background worker?
Calling CancelAsync() followed by RunWorkerAsync() clearly won't do it as their names imply.
Background info:
I have a background worker which calculates a total in my .net 2.0 Windows Forms app.
Whenever the user modifies any value which is part of this total I'd like to restart the background worker in case it would be running so that directly the latest values are considered.
The backgriound work itself does not do any cancleing.
When you call bgw.CancelAsync it sets a flag on the background worker that you need to check yourself in the DoWork handler.
something like:
bool _restart = false;
private void button1_Click(object sender, EventArgs e)
{
bgw.CancelAsync();
_restart = true;
}
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 300; i++)
{
if (bgw.CancellationPending)
{
break;
}
//time consuming calculation
}
}
private void bgw_WorkComplete(object sender, eventargs e) //no ide to hand not sure on name/args
{
if (_restart)
{
bgw.RunWorkerAsync();
_restart = false;
}
}
There are a couple of options, it all depends on how you want to skin this cat:
If you want to continue to use BackgroundWorker, then you need to respect the model that has been established, that is, one of "progress sensitivity". The stuff inside DoWork is clearly required to always be aware of whether or not the a pending cancellation is due (i.e., there needs to be a certain amount of polling taking place in your DoWork loop).
If your calculation code is monolithic and you don't want to mess with it, then don't use BackgroundWorker, but rather fire up your own thread--this way you can forcefully kill it if needs be.
You can hook the change event handler for the controls in which the values are changed and do the following in the handler:
if(!bgWrkr.IsBusy)
//start worker
else if(!bgWrkr.CancellationPending)
bgWrkr.CancelAsync();
Hope it helps you!
I want to leave my requests running, but no longer care about the results. I override the value of the background worker (my busy spinner is using the isBusy flag).
private void SearchWorkerCreate() {
this.searchWorker = new BackgroundWorker();
this.searchWorker.DoWork += this.SearchWorkerWork;
this.searchWorker.RunWorkerCompleted += this.SearchWorkerFinish;
}
private void SearchWorkerStart(string criteria){
if(this.searchWorker.IsBusy){
this.SearchWorkerCreate();
}
this.searchWorker.RunWorkerAsync(criteria);
this.OnPropertyChanged(() => this.IsBusy);
this.OnPropertyChanged(() => this.IsIdle);
}
May this method help someone... I've created a function to reset the backgroundworker in one method. I use it for task to do periodically.
By creating a Task, the backgroundworker is can be stopped with the CancelAsync and restarted inside the Task. Not making a Task wil start the backgroundworker again before it is cancelled, as the OP describes.
The only requirement is that your code runs through some loop, which checks the CancellationPending every period of time (CheckPerMilliseconds).
private void ResetBackgroundWorker()
{
backgroundWorker.CancelAsync();
Task taskStart = Task.Run(() =>
{
Thread.Sleep(CheckPerMilliseconds);
backgroundWorker.RunWorkerAsync();
});
}
Inside the backgroundworker I use a for-loop that checks the CancellationPending.
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
while(true)
{
if (backgroundWorker.CancellationPending)
{
return;
}
//Do something you want to do periodically.
for (int i = 0; i < minutesToDoTask * 60; i++)
{
if (backgroundWorker.CancellationPending)
{
return;
}
Thread.Sleep(CheckPerMilliseconds);
}
}
}

Categories

Resources