I am having a trouble with background worker
private void backgroundPBar_ProgressChanged(object sender, ProgressChangedEventArgs e)
The function doesn't execute when backgroundPBar.ReportProgress(value);
here's the code:
private void backgroundPBar_DoWork(object sender, DoWorkEventArgs e) {
while (fileTransfer.busy) {
if (fileTransfer.sum > 0) {
int value = Convert.ToInt32((fileTransfer.sum * 100) / fileTransfer.fileSize);
backgroundPBar.ReportProgress(value);
Console.WriteLine(value);
}
}
}
private void backgroundPBar_ProgressChanged(object sender, ProgressChangedEventArgs e) {
progressBarFile.Value = e.ProgressPercentage;
this.Text = e.ProgressPercentage.ToString() + "%";
}
How can I fix it?
Make sure you have the progress event wired up:
backgroundPBar.ProgressChanged += backgroundPBar_ProgressChanged
From you description, it seems like it isn't. The assumption here is that fileTransfer.busy is true and fileTransfer.sum > 0 is also true.
Also, make sure you have your the properties of the background worker set:
backgroundPBar.WorkerReportsProgress = true;
(as I see "The Mask" mentioned").
don't forget that to use ReportProgress() method you must set WorkerReportsProgress as true.
I've been having problems getting the ProgressChanged event to fire immediately when the ReportProgress method is called. Because they are seperate threads, I had to resort to thread synchronization. I used a couple WaitHandles and the SignalAndWait method to synchronize the two threads. I kinda defeats the purpose of the background worker, but if I really want the UI thread to do something on time, it really has to make the background worker wait until the UI thread got the message and performed the work.
Private ReportProgressWaitEvent As New AutoResetEvent(False)
Private ReportProgressReadyWaitEvent As New ManualResetEvent(False)
Private Sub ReportProgress(backgroundWorkerProgressEvents As BackgroundWorkerProgressEventsEnum, userState As Object)
ReportProgressReadyWaitEvent.Reset()
_backgroundWorker.ReportProgress(backgroundWorkerProgressEvents, userState)
WaitHandle.SignalAndWait(ReportProgressReadyWaitEvent, ReportProgressWaitEvent)
End Sub
Private Sub _backgroundWorker_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles _backgroundWorker.ProgressChanged
... do work ...
ReportProgressReadyWaitEvent.WaitOne()
ReportProgressWaitEvent.Set()
End Sub
Here's a tutorial of how to use backgroundworker with ProgressBar
You should call (RunWorkerAsync) first then the backgroundPBar_DoWork will start executing its code.
backgroundPBar.RunWorkerAsync();
Related
Ok, I've been banging my head against this all day. I'm still fairly new to programming, and its quite possible that my entire approach here is misguided. But anyway...so I have a simple gui app with a listbox full of folders, and I'm executing an operation on every file in each folder sequentially. This is a very long operation, so I have two progress bars - one for each file, and one for each folder.
private void buttonApplySelected_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
double percentToIncrement = 100.0 / Convert.ToDouble(selectedDirList.Count);
double percentComplete = percentToIncrement;
folderProgressBar.Value = 0;
foreach (string dir in selectedDirList)
{
engine = new OEngine.OEngine(dir, backgroundWorker1);
engine.ProcessSelected(processType);
int percentCompleteInt = Convert.ToInt32(percentComplete);
folderProgressBar.Value = percentCompleteInt;
percentComplete += percentToIncrement;
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
fileProgressBar.Value = e.ProgressPercentage;
}
The BackgroundWorker itself is passed down to the engine, and it updates its progress within the code of the engine processing that folder. (This is probably my first mistake.) The UI catches that ProgressChanged event and updates the fileProgressBar in it's own thread.
But the folderProgressBar needs to be updated once every pass through the for loop, but it gives me Cross-thread operation not valid: Control 'folderProgressBar' accessed from a thread other than the thread it was created on.
If I move it out of the for loop, it wont update after each folder.
If I move all the UI updating out of the DoWork function, and call the DoWork function in a for loop instead, it obviously doesnt wait for each folder to finish and I get "the worker is still busy" exception.
Any ideas?
The general solution for windows form:
Use
WindowsFormsSynchronizationContext syncContext = new WindowsFormsSynchronizationContext();
...
//in the background work or any non UI Thread
//Trigger an update in the GUI thread; one can also use syncContext.Send (depending on the need for syncrhonous or async operation)
syncContext.Post(UpdateGUI, userData);
...
void UpdateGUI(object userData)
{
//update your progress bar
}
If using wpf, declare a variable syncContex.
SynchronizationContext syncContext;
//And when in the UI thread, set the variable's value as
syncContext = SynchronizationContext.Current;
//Then from the non-UI thread,
syncContext.Post(UpdateGUI, userData);
I have a form that spawns a BackgroundWorker, that should update form's own textbox (on main thread), hence Invoke((Action) (...)); call.
If in HandleClosingEvent I just do bgWorker.CancelAsync() then I get ObjectDisposedException on Invoke(...) call, understandably. But if I sit in HandleClosingEvent and wait for bgWorker to be done, than .Invoke(...) never returns, also understandably.
Any ideas how do I close this app without getting the exception, or the deadlock?
Following are 3 relevant methods of the simple Form1 class:
public Form1() {
InitializeComponent();
Closing += HandleClosingEvent;
this.bgWorker.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
while (!this.bgWorker.CancellationPending) {
Invoke((Action) (() => { this.textBox1.Text = Environment.TickCount.ToString(); }));
}
}
private void HandleClosingEvent(object sender, CancelEventArgs e) {
this.bgWorker.CancelAsync();
/////// while (this.bgWorker.CancellationPending) {} // deadlock
}
The only deadlock-safe and exception-safe way to do this that I know is to actually cancel the FormClosing event. Set e.Cancel = true if the BGW is still running and set a flag to indicate that the user requested a close. Then check that flag in the BGW's RunWorkerCompleted event handler and call Close() if it is set.
private bool closePending;
protected override void OnFormClosing(FormClosingEventArgs e) {
if (backgroundWorker1.IsBusy) {
closePending = true;
backgroundWorker1.CancelAsync();
e.Cancel = true;
this.Enabled = false; // or this.Hide()
return;
}
base.OnFormClosing(e);
}
void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
if (closePending) this.Close();
closePending = false;
// etc...
}
I've found another way. If you have more backgroundWorkers you can make:
List<Thread> bgWorkersThreads = new List<Thread>();
and in every backgroundWorker's DoWork method make:
bgWorkesThreads.Add(Thread.CurrentThread);
Arter that you can use:
foreach (Thread thread in this.bgWorkersThreads)
{
thread.Abort();
}
I used this in Word Add-in in Control, which i use in CustomTaskPane. If someone close the document or application earlier then all my backgroundWorkes finishes their work, it raises some COM Exception(I don't remember exatly which).CancelAsync() doesn't work.
But with this, I can close all threads which are used by backgroundworkers Immediately in DocumentBeforeClose event and my problem is solved.
Here was my solution (Sorry it's in VB.Net).
When I run the FormClosing event I run BackgroundWorker1.CancelAsync() to set the CancellationPending value to True. Unfortunately, the program never really gets a chance to check the value CancellationPending value to set e.Cancel to true (which as far as I can tell, can only be done in BackgroundWorker1_DoWork).
I didn't remove that line, although it doesn't really seem to make a difference.
I added a line that would set my global variable, bClosingForm, to True. Then I added a line of code in my BackgroundWorker_WorkCompleted to check both e.Cancelled as well as the global variable, bClosingForm, before performing any ending steps.
Using this template, you should be able to close your form out at any time even if the backgroundworker is in the middle of something (which might not be good, but it's bound to happen so it might as well be dealt with). I'm not sure if it's necessary, but you could dispose the Background worker entirely in the Form_Closed event after this all takes place.
Private bClosingForm As Boolean = False
Private Sub SomeFormName_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
bClosingForm = True
BackgroundWorker1.CancelAsync()
End Sub
Private Sub backgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
'Run background tasks:
If BackgroundWorker1.CancellationPending Then
e.Cancel = True
Else
'Background work here
End If
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
If Not bClosingForm Then
If Not e.Cancelled Then
'Completion Work here
End If
End If
End Sub
Can you not wait on the signal in the destructor of the form?
AutoResetEvent workerDone = new AutoResetEvent();
private void HandleClosingEvent(object sender, CancelEventArgs e)
{
this.bgWorker.CancelAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (!this.bgWorker.CancellationPending) {
Invoke((Action) (() => { this.textBox1.Text =
Environment.TickCount.ToString(); }));
}
}
private ~Form1()
{
workerDone.WaitOne();
}
void backgroundWorker1_RunWorkerCompleted( Object sender, RunWorkerCompletedEventArgs e )
{
workerDone.Set();
}
Firstly, the ObjectDisposedException is only one possible pitfall here. Running the OP's code has produced the following InvalidOperationException on a substantial number of occasions:
Invoke or BeginInvoke cannot be called
on a control until the window handle
has been created.
I suppose this could be amended by starting the worker on the 'Loaded' callback rather than the constructor, but this entire ordeal can be avoided altogether if BackgroundWorker's Progress reporting mechanism is used. The following works well:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (!this.bgWorker.CancellationPending)
{
this.bgWorker.ReportProgress(Environment.TickCount);
Thread.Sleep(1);
}
}
private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.textBox1.Text = e.ProgressPercentage.ToString();
}
I kind of hijacked the percentage parameter but one can use the other overload to pass any parameter.
It is interesting to note that removing the above sleep call clogs the UI, consumes high CPU and continually increases the memory use. I guess it has something to do with the message queue of the GUI being overloaded. However, with the sleep call intact, the CPU usage is virtually 0 and the memory usage seems fine, too. To be prudent, perhaps a higher value than 1 ms should be used? An expert opinion here would be appreciated... Update: It appears that as long as the update isn't too frequent, it should be OK: Link
In any case, I can't foresee a scenario where the updating of the GUI has to be in intervals shorter than a couple of milliseconds (at least, in scenarios where a human is watching the GUI), so I think most of the time progress reporting would be the right choice
I really dont see why DoEvents is regarded as such a bad choice in this case if you are using this.enabled = false. I think it would make it quite neat.
protected override void OnFormClosing(FormClosingEventArgs e) {
this.Enabled = false; // or this.Hide()
e.Cancel = true;
backgroundWorker1.CancelAsync();
while (backgroundWorker1.IsBusy) {
Application.DoEvents();
}
e.cancel = false;
base.OnFormClosing(e);
}
Your backgroundworker should not use Invoke to update the textbox. It should ask the UI thread nicely to update the textbox using event ProgressChanged with the value to put in the textbox attached.
During event Closed (or maybe event Closing), the UI thread remembers that the form is closed before it cancels the backgroundworker.
Upon receiving the progressChanged the UI thread checks if the form is closed and only if not, it updates the textbox.
This won't work for everyone, but if you are doing something in a BackgroundWorker periodically, like every second or every 10 seconds, (perhaps polling a server) this seems to work well to stop the process in an orderly manner and without error messages (at least so far) and is easy to follow;
public void StopPoll()
{
MyBackgroundWorker.CancelAsync(); //Cancel background worker
AutoResetEvent1.Set(); //Release delay so cancellation occurs soon
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
while (!MyBackgroundWorker.CancellationPending)
{
//Do some background stuff
MyBackgroundWorker.ReportProgress(0, (object)SomeData);
AutoResetEvent1.WaitOne(10000);
}
}
I'd pass in the SynchronizationContext associated with the textbox to the BackgroundWorker and use that to perform Updates on the UI thread. Using SynchronizationContext.Post, you can check if the control is disposed or disposing.
What about Me.IsHandleCreated?
Private Sub BwDownload_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BwDownload.RunWorkerCompleted
If Me.IsHandleCreated Then
'Form is still open, so proceed
End If
End Sub
Another way:
if (backgroundWorker.IsBusy)
{
backgroundWorker.CancelAsync();
while (backgroundWorker.IsBusy)
{
Application.DoEvents();
}
}
One solution that works, but too complicated. The idea is to spawn the timer that will keep trying to close the form, and form will refuse to close until said bgWorker is dead.
private void HandleClosingEvent(object sender, CancelEventArgs e) {
if (!this.bgWorker.IsBusy) {
// bgWorker is dead, let Closing event proceed.
e.Cancel = false;
return;
}
if (!this.bgWorker.CancellationPending) {
// it is first call to Closing, cancel the bgWorker.
this.bgWorker.CancelAsync();
this.timer1.Enabled = true;
}
// either this is first attempt to close the form, or bgWorker isn't dead.
e.Cancel = true;
}
private void timer1_Tick(object sender, EventArgs e) {
Trace.WriteLine("Trying to close...");
Close();
}
Can some kind soul please explain why the following psudocode would not work. The problem is that the lock can never be aquired on the backgroundWorker (Monitor.TryEnter(bw)) when called from a new thread. If I call it from the main UI thread it works fine.
Thanks
P
public class MyClass
{
private BackgroundWorker bw;
private void Button_Click(object sender, EventArgs e)
{
System.Threading.Thread t =
new System.Threading.Thread(new System.Threading.ThreadStart(DoStuff));
t.IsBackground = true;
t.Start();
}
private void DoStuff()
{
if (Monitor.TryEnter(bw))
{
WorkDetails wd = new WorkDetails('some stuff here');
bw.RunWorkerAsync(wd);
// etc... etc...
}
}
}
Are you missing a Monitor.Exit at the end of the if block.
Without a Monitor.Exit, whichever thread first does a Monitor.TryEnter successfully will be the only thread that can enter again.
I'm not sure you are using the background worker (BGW) as it was intended
The idea behind it, usually, is that you don't create threads yourself but rather specify to the BGW what you want to be done asynchronously. so your code should look like:
private BackgroundWorker bw = new BackgroundWorker ();
ctor
{
bw.DoWork += (sender, e) => DoStuff();
}
private void Button_Click(object sender, EventArgs e)
{
bw.RunWorkerAsync();
}
For more information, see here
comments:
As a rule of thumb, never lock on arbitrary objects (like you do on bw) but rather on objects whose sole purpose is locking. I recommend you read Jon Skeet's excellent multi threading guide
You can add the BGW declaratively via the designer, saving yourself the instantiation and event hooking code
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)
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);
}
}
}