I have placed a check in the system
public void ProcesssMessages(ResponseEventArgs message)
{
_queue.Enqueue(message);
if (!backgroundWorker1.IsBusy)
{
backgroundWorker1.RunWorkerAsync();
}
}
Still it gives me error backgroundworker1 is already running and you can't run it twice. This is the only point in my screen where I run the background worker so there is no way someone in the meantime run it from other section.
Is there someway to make it safe or fail-proof.
I am not handling the completed event and thus there could be no way I am blocking that event.
I cant run it in completed event as this is in a user control and someone access a method in this user control and I add that message in a queue. I then need to process that message using background worker, but first need to check if background worker is already processing the queue. Code for the background worker is
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (_queue.Count > 0)
{
ResponseEventArgs currentMessage = _queue.Dequeue();
if (currentMessage != null)
{
AddUpdateDataRow(currentMessage);
UpdateMainUI();
}
}
}
Code for the UpdateMainUI method
private void UpdateMainUI()
{
if (gridControl1.InvokeRequired)
{
gridControl1.Invoke(new MethodInvoker(UpdateMainUI));
}
gridControl1.RefreshDataSource();
}
Thanks
Related
How can I wait for BackgroundWorker to finish, if running, when the user request to close application? I'd like to wait to this BackgroundWorker finish then exit application. I tried with AutoResetEvent but a call to WaitOne() at FormClosing time seems to block the entire UI and doesn't fire RunWorkerCompleted event where Set() is called.
How can I accomplish this?
I'm looking for an alternative/proper way for this:
bool done = false;
private void my_backgroundWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
resetEvent.Set();
done = true;
}
private void myForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (notify_backgroundWorker.IsBusy)
{
while(!done)
{
Application.DoEvents();
Thread.Sleep(500);
}
//resetEvent.WaitOne();
}
}
No need to make it so complex, just have a class level variable
bool quitRequestedWhileWorkerBusy=false;
If the user tried to close the form, in the form closing event check if the worker isbusy, cancel the event and set quitRequestedWhileWorkerBusy=true
In your worker completed event, if(quitRequestedWhileWorkerBusy) this.Close();
Another way is based on OP's sample code, but simplified:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
// Stop the background worker thread (if running) to avoid race hazard.
if (backgroundWorker1.IsBusy)
{
backgroundWorker1.CancelAsync();
// Wait for the background worker thread to actually finish.
while (backgroundWorker1.IsBusy)
{
Application.DoEvents();
Thread.Sleep(100);
}
}
}
I have the following code. It is just a form app. On load it will run the bacground worker.
Then I have a button that is supposed to stop the infinite loop in the background worker by setting a flag to true.
I'm logging the out put of the backgroundworker1.IsBusy and it says it is busy but according to the logic in my code it shouldn't be busy because I set the flag to true thus exiting the while loop and running the backgroundworker_Completed event.
I must be doing something wrong but I can not figure it out.
If I'm approaching this incorrectly could somebody either help me fix what I'm doing wrong or point me in a better direction on how I can accomplish what I"m trying to do here.
private volatile bool StopScanning = false;
private void myForm_Load(object sender, EventArgs e)
{
try
{
if (backgroundWorker1.IsBusy)
{
//do nothing
}
else
{
backgroundWorker1.RunWorkerAsync();
}
}
catch (Exception boo)
{
Log.log(boo.ToString());
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (StopScanning == false)
{
Application.DoEvents();
try
{
ReturnScannedItems();
System.Threading.Thread.Sleep(1000);
}
catch (Exception boo)
{
Log.log(boo.ToString());
}
}
}
private void cancelbutton_Click(object sender, EventArgs e)
{
try
{
Log.log("Setting Stop Scan flag to true");
StopScanning = true;
Log.log(CloseScanSession().ToString());
}
catch (Exception boo)
{
Log.log("Setting Stop Scan flag to true");
StopScanning = true;
Log.log(CloseScanSession().ToString());
Log.log(boo.ToString());
}
while (backgroundWorker1.IsBusy)
{
Log.log("Still busy");
}
this.Close();
}
You are blocking the UI thread, which prevents the BackgroundWorker from completing. It can't raise the RunWorkerCompleted event until the UI thread is free to process new messages (raising the event involves posting a message to the UI thread's message queue, so that the UI thread can then execute the code that will actually raise the event).
Your code also is flawed in that it's calling Application.DoEvents() from the worker thread. You should never call this method anyway, but it's particularly foolish to call it from a worker thread, because the whole point of having a worker thread is to avoid having to call that method (and it won't do anything when called on the worker thread anyway, because the worker thread shouldn't own any window objects that would need to receive a window message).
Instead of sitting in a busy loop, checking IsBusy and blocking the UI thread, you should just subscribe to the RunWorkerCompleted event and do whatever you need to do there. Without a good Minimal, Complete, and Verifiable code example that fully illustrates what you're actually trying to do, it's not possible to provide any more specific advice than that.
As in title, I have some bgw I want to call on every button press.
Is this code correct ?
private static BackgroundWorker bgw = null;
private void bttn_Click(...)
{
if(!bgw.IsBusy)
doSomeWorkInBg();
else
MessageBox.Show("Slow down a bit");
}
private void doSomeWorkInBg()
{
if (bgw == null)
{
bgw = new BackgroundWorker();
bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
}
bgw.RunWorkerAsync();
}
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
// do some work that takes time
}
Or maybe I should add RunWorkerCompleted event with "bgw = null;" code or something like that?
You should avoid making your bgw static, because you are using it in non-static context.
When I need to re-run the worker frequently based on the UI event, this is the construct that I usually use:
bool ShouldRunWorkedASAP;
private void bttn_Click(...){
ShouldRunWorkedASAP=true;
if (!bgw.IsBusy) bgw.RunWorkerAsync();
}
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
while (ShouldRunWorkedASAP) {
ShouldRunWorkedASAP=false;
// do some work that takes time
}
}
This assumes that the worked is instantiated in the class constructor. Basically this code sets the flag that the worker should run ASAP whenever the UI event occurred, then tries to run the worker. If it is already running - then the while() loop inside the worker implementation will schedule the job for the next run as soon as it completes.
This code does not ensure that the worker will run exactly the number of times the user presses the button, not sure if in your case this is required or not.
Imagine following situation:
I got a signal on the ui thread from a third party server.
I start a BackgroundWorker with RunAsync to fetch data from a database and another async thread, which shall poll
another hardware and receive signals, also not in ui thread
Inside the bg's DoWork eventhandler I call manualresetEvent.Reset(). Then I call the data-fetching method, and then I call manualresetEvent.Set() and in the end I call the method METH_UI_1 on the ui thread by invoking it.
The other hardware thread shall receive hardware-data, which then itself is passed via Invoke to the ui into the ui thread to set some ui-elements periodically depending on the hardware-data I get.
The data from database can also not be fetched yet, but the ui must react to the hardware-data, which is polled by the second async thread.
In METH_UI_1 I call manualresetEvent.WaitOne();
Some times I get the exception, that the background worker is busy and cannot run multiple tasks concurrently.
a) Is there really a need for a ManualResetEvent object ?
b) Would it be enough, to check for the isBusy property in order to issue WaitOne() only, when the background worker is no more busy ?
UPDATE: CODE.
MainForm.cs (event handler of third party hw-vendor, component, handled in ui thread)
private void thrdptyPlcGotData(object sender, thrdptyPlcGotDataEventArgs e)
{
string strError = string.Empty;
bool blNotReadyYet = false;
try
{
ThrdPtyPlcIfs.DataSetthrdptyPlc ds;
ds = new ThrdPtyPlcIfs.Dataset();
e.FillDataToTDataSet(ds);
ThrdPtyPlcIfs.Statics.SaveDataSet(ds, CLStatics.FileName);
if (this.ValidateDsDetail(ds))
{
// begin async work..... ask db, continue asking scale-> inside got weight of scale the rest is handled ( using or trashing db data )
this.ExtractDataOfDataSet(ds);
this.bgWorkerStart_Get_Data.RunWorkerAsync();
_oAsyncScaleManager.StartThread();
}
}
}
runworkerasynch does this:
private void bgWorkerStart_Get_Data_RFC_DoWork(object sender, DoWorkEventArgs e)
{
try
{
_blnStart_Get_Data_RFC = this.StartGetData_RFC(null);
}
catch (Exception ex)
{
LogExcep(ex);
_blnStart_Get_Data_RFC = false;
}
}
WorkCompleted EventHandler of the BackGroundWorker:
private void bgWorkerStart_Get_Data_RFC_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
try
{
if (InvokeRequired)
{
this.Invoke((MethodInvoker)delegate()
{
this.ApplyDbDataToUi();
}
);
}
else
{
this.ApplyDbDataToUi();
}
}
catch (Exception ex)
{
LogAndShowExep(ex);
}
}
As rare as it might be , its possible the BackgrounWorker isn't finished when you set manualresetEvent inside the dowork method block. If its at the very end , I would hook into the backgroundworker workcompleted event and set it in there.
This is how I set up my Background Worker
The question is: How do I terminate the process midway? I have to create a cancel button that will stop current processing.
I thought of use the line below...
if ((worker.CancellationPending == true))
{
e.Cancel = true;
}
...and encapsulating the AllocateTrades2 method inside, but that would mean I have to call the Cancel Flag from somewhere else, and it will check it only after the method is done processing.
And I can't just "pretend" that it is not doing anything because if the user clicks the same operation again, an exception will be thrown that the worker is in use.
An example of what I want to do:
public void bw_DoWork(object sender, DoWorkEventArgs e)
{
AllocateTrades2();
}
public void AllocateTrades()
{
Method1();
CheckTerminate();
Method2();
CheckTerminate();
//Click the cancel button in the UI during method 3 processing
Method3();
CheckTerminate();
}
And the CheckTerminate() stops all of its processing and makes itself available for future calls. Is what I'm trying to do possible?
You'd have to do something like...
public void AllocateTrades()
{
if (!worker.CancellationPending)
{
Method1();
}
if (!worker.CancellationPending)
{
Method2();
}
if (!worker.CancellationPending)
{
Method3();
}
}
.Net cannot decide for you what is an "atomic" operation with respect to your background work, so you must decide how often and where to check for a pending cancellation.
The answer is YES.
Wherever you check for 'CancellationPending', you can stop your worker.
In your example you have these options:
check in the 'AllocateTrades2()'-Function itself
If (bw.CancellationPending) {
e.Cancel = True
return; }
Note: If you check it here, you can cancel the process 'midway'.
check from outside, before you run your next process
If (!worker.CancellationPending) {
MethodX(); }
Generally, you should check for CancellationPending wherever it makes sense (e.g. in a loop: after or before an item has been processed).