Cannot stop the BackgroundWorker - c#

I know that this question has posted several times, but this situation is different. So supposed that I'm executing a method that need to iterate through several items (database rows), this require a lot of time.
Now in my BackgroundWorker I need in some case to stop the synchronization, in particular when the user press a button. What I did in the _DoWork event is this:
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
while (!worker.CancellationPending)
{
LongOperation();
}
}
now the problem's that when I call worker.CancelAsync() the LongOperation() continue the execution but shouldn't! 'cause the while have the condition of CancellationPending. I saw in the net that this solution is thread-safe, so maybe am I doing something wrong?

All you need is the following structure
private void runButton_Click(object sender, EventArgs e)
{
worker=new BackgroundWorker();
worker.WorkerSupportsCancellation=true;
worker.RunWorkerCompleted+=Bk_RunWorkerCompleted;
worker.DoWork+=Bk_DoWork;
worker.RunWorkerAsync();
}
private void cancelButton_Click(object sender, EventArgs e)
{
worker.CancelAsync();
}
void ReallyReallyLongOperation(BackgroundWorker worker)
{
...within a loop
if(worker.CancellationPending)
{
return;
}
}
private void Bk_DoWork(object sender, DoWorkEventArgs e)
{
ReallyReallyLongOperation(worker);
if(worker.CancellationPending)
{
e.Cancel = true;
}
}
private void Bk_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(!e.Cancelled)
{
...
}
}

the LongOperation() continue the execution but shouldn't! 'cause the while have the condition of CancellationPending.
No, it should continue execution! You are completely wrong with understanding of the while check. It does not check every second for the cancellation, it does the check only before starting the LongOperation!
So the only thing you can do in such situation is to check the worker.CancellationPending property inside the LongOperation method, not outside of it.

Related

Pass different method/function to DoWork event of Background worker

Please bear with my ignorance on this subject and on the terminology I have used. Please correct me where I am incorrect.
I have used a background Worker from the toolbox onto my form and I am passing a method to the DoWork event. What I have understood in this first attempt with background Worker is that I can use the background Worker that I've created only for 1 task. See code below:
private void btn1_Click(object sender, EventArgs e)
{
// Should call the uploadToDB1 using BackgroundWorker's DoWork event.
backgroundWorker1.RunWorkerAsync();
}
private void btn2_Click(object sender, EventArgs e)
{
// Should call the uploadToDB2 using BackgroundWorker's DoWork event.
backgroundWorker1.RunWorkerAsync();
}
private void uploadToDB1()
{
// Code for uploading to DB 1.
}
private void uploadToDB2()
{
// Code for uploading to DB 2.
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
try
{
uploadToDB1(); // I want to change this to uploadToDB2 i.e. a variable method, How do I assign a call to this?
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Visible = true;
progressBar1.Maximum = maxRecords;
lblProgress.Text = Convert.ToString(e.ProgressPercentage.ToString() + "/" + maxRecords);
progressBar1.Value = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressBar1.Visible = false;
lblProgress.Text = "";
}
I need to be able to dynamically pass a method to the DoWork event without having the need to create multiple background Workers as the actions in the rest of the events related to the background Worker remains unchanged.
Could you please advise how I should go about doing this?
Updated Code using TPL, however I am getting a cross thread error. Could you please help with a corrected code? Upload to DB 2 should happen only after upload to DB 1 is complete. So each time an upload happens the label and progress bar needs to be updated. I also need to pass different text to the label.
private void btn1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(uploadToDB1);
}
private void uploadToDB1()
{
for(i=0;i<dt.rows.count-1;i++)
{
// Code for inserting into DB1.
progressbar1.maximum=dt.rows.count-1;
progressbar1.value=i;
}
uploadToDB2();
}
private void uploadToDB2()
{
for(i=0;i<dt.rows.count-1;i++)
{
// Code for inserting into DB2.
progressbar1.maximum=dt.rows.count-1;
progressbar1.value=i;
}
}
What you can do, and is a bit of a hack, is pass an Action as an argument for invocation to your DoWorkAsync:
var bw = new BackgroundWorker();
bw.DoWork += (s, o) =>
{
Action actualWork = (Action)o.Argument;
actualWork();
}
and when you invoke DoWorkAsync:
Action action = () => DoSomething();
bw.RunWorkerAsync(action);
Instead, as #Sriram suggested, look into the Task Parallel Library, which will make you life a bit easier:
private async void btn1_Click(object sender, EventArgs e)
{
await Task.Run(UpdateFirst);
// Update UI here.
}
private async void btn2_Click(object sender, EventArgs e)
{
await Task.Run(UpdateSecond);
// Update UI again.
}
An extensive answer on TPL and the use of IProgess<T> can be found in How to provide a feedback to UI in a async method?

Breaking from a loop with button click - C#

I have a question regarding looping with button click event, I've tried many methods & searched many pages in search for a simple answer for the past hour, but the truth is each answer just looks like alien code, probably because I'm still very new to developing.
Here's a simplified version of what I'm trying to do :
private string Message = "Hello";
private void Spam(bool loop)
{
if (loop == true)
{
while (loop == true)
{
MessageBox.Show(Message);
}
}
else { MessageBox.Show("Spamming has stopped !! "); }
}
private void button1_Click(object sender, EventArgs e)
{
Spam(true);
}
private void button2_Click(object sender, EventArgs e)
{
Spam(false);
}
Obviously this isn't my API, or it'd be a useless thing to invent, however, the code itself is long & you guys always ask for "relevant code" (No disrespect), so there it is.
My problem : Breaking out of the spam loop upon clicking button 2, the code to me looks decent enough for the API to figure out, but each time button 1 is clicked, the API freezes.
Use a background worker to do your work. You can use the cancellation feature to break out of it when you're done. Your loop as you have it will block the UI thread when executed syncronously, which is why your GUI becomes unresponsive. Note if you do any interaction with the UI in the do work delegate, you need to marshal back onto the UI thread (via invoke for example).
private BackgroundWorker _worker = null;
private void goButton_Click(object sender, EventArgs e)
{
_worker = new BackgroundWorker();
_worker.WorkerSupportsCancellation = true;
_worker.DoWork += new DoWorkEventHandler((state, args) =>
{
do
{
if (_worker.CancellationPending)
break;
Console.WriteLine("Hello, world");
} while (true);
});
_worker.RunWorkerAsync();
goButton.Enabled = false;
stopButton.Enabled = true;
}
private void stopButton_Click(object sender, EventArgs e)
{
stopButton.Enabled = false;
goButton.Enabled = true;
_worker.CancelAsync();
}
Update 2019:
BackgroundWorker is now largely obsolete, replaced by the async/await feature in later versions of C# which is easier to use. Here is an example of how to achieve the same thing using that feature:
private CancellationTokenSource _canceller;
private async void goButton_Click(object sender, EventArgs e)
{
goButton.Enabled = false;
stopButton.Enabled = true;
_canceller = new CancellationTokenSource();
await Task.Run(() =>
{
do
{
Console.WriteLine("Hello, world");
if (_canceller.Token.IsCancellationRequested)
break;
} while (true);
});
_canceller.Dispose();
goButton.Enabled = true;
stopButton.Enabled = false;
}
private void stopButton_Click(object sender, EventArgs e)
{
_canceller.Cancel();
}
There's one important thing to remember:
While your code is being executed, the user cannot interact with your user interface.
That means: You first need to exit the loop (i.e. return from the Spam method), and then the user can click Button2.
That's a hard truth, because it means you cannot write the code in the way you wanted to. Fortunately, there are a few ways to work around that:
Don't use a loop. Use some kind of timer to do the "spamming". Button1 starts the timer, Button2 stops it. What kind of timer is available depends on the user interface library you use (WinForms has a Timer, WPF has a DispatcherTimer).
Do the "spamming" in a background thread. This will allow your user interface to stay responsive, and you can communicate with the background thread, for example, by setting a volatile Boolean. This, however, is an advanced topic (and can quickly lead to complex synchronization issues), so I suggest that you try the other option first.
When you click button1 the Spam method is called and loop is starting. When you click button2 Spam method is called but it's not the same. It's the second execution, so it will check the condition and won't enter into the loop, but the loop in the first call sill will be running.
You should use a flag and the loop should use that flag to determine whether it should be still running. It should look something like that:
bool run = false;
string message = "This API is not original";
private void Spam()
{
while (run == true)
{
MessageBox.Show(message);
}
}
}
private void button1_Click(object sender, EventArgs e)
{
message = "Hellooo";
flag = true;
Spam();
}
private void button2_Click(object sender, EventArgs e)
{
flag = false;
}
Take a look at this concept:
private bool loop = false;
private void Start()
{
loop = true;
Spam("Some Message??");
}
private void Spam(string message)
{
while (loop)
{
MessageBox.Show("This API is not original");
}
}
private void button1_Click(object sender, EventArgs e)
{
loop = true;
}
private void button2_Click(object sender, EventArgs e)
{
loop = false;
}
However, the user won't be able to press a button if a MessageBox keeps popping up as it takes up the main UI thread. In order to prevent this you could use BackgroundWorker or start a new thread.

how can I use BackGroundWorker to make continuous execution for a method which is depends on user input to stop? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How To Start And Stop A Continuously Running Background Worker Using A Button
I have 2 buttons the first one it's name "Continuous" .. the second one "Stop"
I want to call a method when press the continuous button :
private void continuous_Click(object sender ,EvantArgs e)
{
// continuous taking pictures ...
}
my question is : how can I stop the execution by pressing the stop button ??
I've written a code to take a picture and I've succeeded to take pictures ...
now I want the camera to take continuous snapshots ... but if I press stop button the camera should stop taking pictures ...
I've used BackGroundWorker but the code does not work !!!
this is the code :
private void ContinousSnaps_Click(object sender, EventArgs e)
{
Contiguous.DoWork += Contiguous_DoWork;
Contiguous.RunWorkerCompleted += Contiguous_RunWorkerCompleted;
Contiguous.RunWorkerAsync();
}
private void Contiguous_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; ; i++) TakeSnapShotCommand();
}
private void Contiguous_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
MessageBox.Show("complete");
}
//------------------------------------------------------------------//
private void Stop_Click(object sender, EventArgs e)
{
Contiguous.CancelAsync();
}
//--------------------------------------------------------------------//
how can I achieve the result that I want ?!
Try and see if this is going to work:
In your _DoWork event:
private void Contiguous_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; ; i++)
{
if (Contiguous.CancellationPending)
{
e.Cancel = true;
return;
}
TakeSnapShotCommand();
}
}
And in the Stop_Click to the following:
private void Stop_Click(object sender, EventArgs e)
{
if (Contiguous.WorkerSupportsCancellation)
Contiguous.CancelAsync();
}
Also make sure you allow cancellation (and if you want to take my advice here - move these event registrations in a the form load, so they will be executed once, not every time the button is clicked - leave just the Continuous.RunWorkerAsync()):
// your form load <---
private void Form1_Load(object sender, EventArgs e)
{
Contiguous.DoWork += Contiguous_DoWork;
Contiguous.RunWorkerCompleted += Contiguous_RunWorkerCompleted;
Contiguous.WorkerSupportsCancellation = true; // allowing cancellation
}
private void ContinousSnaps_Click(object sender, EventArgs e)
{
// not a bad idea if you disable the button here at this point
Contiguous.RunWorkerAsync();
}

Windows Form Application, Thread won't stop

I am using Windows Form application for my thread demo. When I click on button1 ,It will start the thread and recursively doing a work.
Here the Form will not hang as I expected. I want to Stop the currently running thread when I click on Button2. However this won't work.
private void button1_Click(object sender, EventArgs e)
{
t = new Thread(doWork); // Kick off a new thread
t.Start();
}
private void button2_Click(object sender, EventArgs e)
{
t.Abort();
}
static void doWork()
{
while (true)
{
//My work
}
}
}
.When Im debugging, the button2_Click method won't hit the pointer. I think because Thread is keep busy.
Please correct me if I going wrong somewhere.
You can't kill thread like this. The reason is to avoid situations where you add lock in thread and then kill it before lock is released.
You can create global variable and control your thread using it.
Simple sample:
private volatile bool m_StopThread;
private void button1_Click(object sender, EventArgs e)
{
t = new Thread(doWork); // Kick off a new thread
t.Start();
}
private void button2_Click(object sender, EventArgs e)
{
m_StopThread = true;
}
static void doWork()
{
while (!m_StopThread)
{
//My work
}
}

c# loop every minute - where to put the code?

Currently I'm moving from java to c# and I'm full of crazy questions.
I'm trying new things on a windows form application and now,I would like to create a loop wich is executing a code every 1 minute,the problem is that I have no idea where to put this code.
For example,the form structure is like:
using System;
namespace Tray_Icon
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
notifyIcon1.ShowBalloonTip(5000);
}
private void notifyIcon1_BalloonTipClicked(object sender, EventArgs e)
{
label1.Text = "Baloon clicked!";
}
private void notifyIcon1_BalloonTipClosed(object sender, EventArgs e)
{
label1.Text = "baloon closed!";
}
private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
{
}
private void option1ToolStripMenuItem_Click(object sender, EventArgs e)
{
//some code here
}
private void option2ToolStripMenuItem_Click(object sender, EventArgs e)
{
//some code here
}
private void option3ToolStripMenuItem_Click(object sender, EventArgs e)
{
label1.Text = "Option 3 clicked!";
}
private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
{
option1ToolStripMenuItem_Click(this, null);
}
private void closeToolStripMenuItem_Click(object sender, EventArgs e)
{
this.Close();
}
private void btnWrite_Click(object sender, EventArgs e)
{
//code here
}
}
}
Where should I put the loop code? :(
Thanks in advance for ANY replay!!!
Add a Timer to your form:
set its Interval property to 60000 (one minute in milliseconds) and Enabled to True:
and attach an event handler to the Timer.Tick event, e.g. by double-clicking the timer in the Forms designer:
private void timer1_Tick(object sender, EventArgs e)
{
// do something here. It will be executed every 60 seconds
}
You would have to add a timer, and set the interval to 1000 miliseconds, and in the OnTick event you add the code with your loop
Timer tmr = null;
private void StartTimer()
{
tmr = new Timer();
tmr.Interval = 1000;
tmr.Tick += new EventHandler<EventArgs>(tmr_Tick);
tmr.Enabled = true;
}
void tmr_Tick(object sender, EventArgs e)
{
// Code with your loop here
}
You can't put any loop code in here.
In your designer look for the Timer control. When you have that, configure it to run every minute and place your code in the Timer_Tick event.
Or create a timer manually in code and respond to the event :) But for starters, doing it by the designer is easier!
Drag a Timer component on the Form and doubleclick it. There you go with the code.
The Timer component runs in the main thread so you can modify UI components without worrying.
Alternatively You could create a System.Timers.Timer, which has it's own thread and has some advantages, but possible caveats when modifying UI components. See http://msdn.microsoft.com/en-us/library/system.timers.timer.aspx
Try to use Background Worker and put the code in the backgroundWorker.DoWork or use a Timer
Use System.Timers.Timer:
System.Timers.Timer aTimer;
{
aTimer = new System.Timers.Timer();
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
aTimer.Interval = 60000;
aTimer.Enabled = true;
}
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
}
for using Timer see this tutorial: C# Timer
How you do it in Java platform?
I think Java should be the same with .net.
In fact, a form program is just normal program which contains a event dispatcher. The event dispatcher listen to the UI events and dispatch them to the event handlers. I think all the UI mode should like this, no matter Java or .net platform.
So generally speaking, you have 2 options:
Start the loop at beginning. In this case, you should insert your
code in the constructor of the Form.
Start the loop when user
click the button. In this case, you should insert your code in the
event handler function.
Yes, as others mentioned, you should use the timer. But this should after you know where your code should locate. You also can use a endless loop with a sleep call. But timer is a better solution.
Idea of timer is more better. But If you want to use threads. Then Follow this
Let me assume that You want to do it right from the start of program
You can write in body of function (event in fact) named Form1_Load as
Your actual code is just within while loop other code only to guide
I can guide if you don't know the use of threads in C#
bool button2Clicked = false;
private void Form1_Load(object sender, EventArgs e)
{
// A good Way to call Thread
System.Threading.Thread t1 = new System.Threading.Thread(delegate()
{
while (!button2Clicked)
{
// Do Any Stuff;
System.Threading.Thread.Sleep(60000); //60000 Millieconds=1M
}
});
t1.IsBackground = true; // With above statement Thread Will automatically
// be Aborted on Application Exit
t1.Start();
}

Categories

Resources