I am confused about the BackGroundWorker RunWorkerCompleted event execution timing.
This is my test code
private string hellow="hello";
private void button1_Click(object sender, EventArgs e)
{
bool createAndRunWorkResult = CreateAndRunWork();
if (createAndRunWorkResult)
{
//Do something that need wait RunBackGroundWorkerCompleted execute here.
//MessageBox.Show(hello);
}
}
private bool CreateAndRunWork()
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
worker.RunWorkerAsync();
return true;
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
//Nothing here;
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
hello="aloha";
}
My design workflow is to click the button1 and then do something after RunWorkerCompleted has executed.
But RunWorkerCompleted seems to be located on the bottom of the method stack. In other words: I'm getting createAndRunWorkResult before RunWorkerCompleted executes. What confuses me is if I uncomment MessageBox.Show(hello) in button1_Click, the MessageBox.Show(hello) will wait until worker_RunWorkerCompleted has executed. But, I still get a "hello" messagebox rather than "aloha".
I guess all UI operation will be located below the RunWorkerCompleted at the method stack.
However, I'm not sure if my assumption is correct and if there is a way to force do something only after RunWorkerCompleted has been executed?
The Problem is, that a backgroudnworker is another thread that you can't wait for.
The backgroundworker is telling you when it's finished its work.
so your code should look like this
private string hellow="hello";
private void button1_Click(object sender, EventArgs e)
{
bool createAndRunWorkResult = CreateAndRunWork();
}
private bool CreateAndRunWork()
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
worker.RunWorkerAsync();
return true;
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
//Nothing here;
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
hellow="aloha";
//Do something that need wait RunBackGroundWorkerCompleted execute here.
//MessageBox.Show(hellow);
}
I recommend reading this: http://msdn.microsoft.com/en-us/library/ms173178%28v=vs.120%29.aspx
about threading
BackgroundWorker object is designed to simply run a function on a different thread and then call an event on your UI thread when it's complete, So in your code you should call function which you want to run after RunWorkerCompleted.
There are 3 Steps:
Create a BackgroundWorker object.
Tell the BackgroundWorker object what task to run on the background thread (the DoWork function).
Tell it what function to run on the UI thread when the work is complete (the RunWorkerCompleted function).
If you write function call in your code
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Test();//This is the code which in your case you want to run
}
void Test()
{
hello = "Hola";
MessageBox.Show(hello);
}
you will get HOLA message this is due to step 3 mentioned above. Hope this helps
Related
I do have experience with software developping in Python (GUI platform PyQt) and I am learing software development in C#. I wanted to know how can I run a thread/task in C# that uses UI objects but keeping the UI "alive" and not keeping the button pressed. I did used "Invoke" method to share UI objects with thread/task and did not call any join method, but still button remain pressed during thread execution. Is there any way to run this method in background, but keeping the GUI responsive?
Thanks in advance!
private async void Button_Click(object sender, RoutedEventArgs e)
{
await Task.Run(new Action(this.Iterate_balance));
}
private async void Iterate_balance()
{
this.Dispatcher.Invoke(() =>
{
// the rest of code
}
}
use async/await pattern properly and you won't need Dispatcher at all:
private async void Button_Click(object sender, RoutedEventArgs e)
{
await Iterate_balance();
}
private async Task Iterate_balance()
{
button.Content = "Click to stop";
// some long async operation
await Task.Delay(TimeSpan.FromSeconds(4));
button.Content = "Click to run";
}
TRY THIS:
1.Add following using: using System.ComponentModel;
2.Declare background worker:
private readonly BackgroundWorker worker = new BackgroundWorker();
3.Register events:
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
4.Implement two methods:
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
// run all background tasks here
}
private void worker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
//update ui once worker complete his work
}
5.Run worker async whenever your need.
worker.RunWorkerAsync();
Also if you want to report process progress you should subscribe to ProgressChanged event and use ReportProgress(Int32) in DoWork method to raise an event. Also set following: worker.WorkerReportsProgress = true;
Hope this help.
Let's say I have a background worker like this:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while(true)
{
//Kill zombies
}
}
How can I make this background worker start and stop using a button on a WinForm?
Maybe you can use a manualresetevent like this, I didn't debug this but worth a shot. If it works you won't be having the thread spin its wheels while it's waiting
ManualResetEvent run = new ManualResetEvent(true);
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while(run.WaitOne())
{
//Kill zombies
}
}
private void War()
{
run.Set();
}
private void Peace()
{
run.Reset();
}
Use the CancelAsync method.
backgroundworker1.CancelAsync();
In your loop inside the worker thread.
if (backgroundWorker.CancellationPending) return;
This doesn't happen immediately.
This is how to do it (link to answer below)
By stop do you really mean stop or do you mean pause?
If you mean stop, then this is a piece of cake. Create a button click event handler for the button you want to be responsible for starting the background worker and a button click event handler for the one responsible for stopping it. On your start button, make a call to the background worker method that fires the do_work event. Something like this:
private void startButton_Click(System.Object sender,
System.EventArgs e)
{
// Start the asynchronous operation.
backgroundWorker1.RunWorkerAsync();
}
On your stop button, make a call to the method that sets the background worker's CancellationPending to true, like this:
private void cancelAsyncButton_Click(System.Object sender,
System.EventArgs e)
{
// Cancel the asynchronous operation.
this.backgroundWorker1.CancelAsync();
}
Now don't forget to check for the CancelationPending flag inside your background worker's doWork. Something like this:
private void KillZombies(BackgroundWorker worker, DoWorkEventArgs e)
{
while (true)
{
if (worker.CancellationPending)
{
e.Cancel = true;
}
}
}
And your doWork method:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
KillZombies(worker, e);
}
I hope this can steer you in the right direction. Some further readings:
http://msdn.microsoft.com/en-us/library/b2zk6580(v=VS.90).aspx
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
http://msdn.microsoft.com/en-us/library/waw3xexc.aspx
I haven't tested this, I have code somewhere that I'll have to see exactly what I did, but something like this is an adaptation of Fredrik's answer:
private bool _performKilling;
private object _lockObject = new object();
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while(true)
{
if (_performKilling)
{
//Kill zombies
}
else
{ //We pause until we are woken up so as not to consume cycles
Monitor.Wait(_lockObject);
}
}
}
private void StartKilling()
{
_performKilling = true;
Monitor.Pulse(_lockObject);
}
private void StopAllThatKilling()
{
_performKilling = false;
]
More complete example of this pattern here:
https://github.com/AaronLS/CellularAutomataAsNeuralNetwork/blob/fe9e6b950e5e28d2c99350cb8ff3157720555e14/CellLifeGame1/Modeling.cs
I need to be able to continuously run my BackgroundWorker. The DoWork event contains a pool threaded process and the OnComplete updates my UI.
I have not been able to find a way to infinitely loop the BackgroundWorker.RunWorkerAsync() method without the whole program freezing. Any help would be greatly appreciated.
You have to make a loop in your DoWork-Method. To update your UI you shoud use the ProgressChanged-Method. Here is a small example how this can look like
public Test()
{
this.InitializeComponent();
BackgroundWorker backgroundWorker = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
backgroundWorker.DoWork += BackgroundWorkerOnDoWork;
backgroundWorker.ProgressChanged += BackgroundWorkerOnProgressChanged;
}
private void BackgroundWorkerOnProgressChanged(object sender, ProgressChangedEventArgs e)
{
object userObject = e.UserState;
int percentage = e.ProgressPercentage;
}
private void BackgroundWorkerOnDoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = (BackgroundWorker) sender;
while (!worker.CancellationPending)
{
//Do your stuff here
worker.ReportProgress(0, "AN OBJECT TO PASS TO THE UI-THREAD");
}
}
I have done this in the past when needing something to run in the background.
If you try to run the backgroundworker while it is running, you will get an excpetion!
That is why i make the BackGroundWorker start itself when it is done in the completed event.
And then it will loop forever.
private void Main_Load(object sender, EventArgs e)
{
// Start Background Worker on load
bgWorker.RunWorkerAsync();
}
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(1000); // If you need to make a pause between runs
// Do work here
}
private void bgCheck_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Update UI
// Run again
bgWorker.RunWorkerAsync(); // This will make the BgWorker run again, and never runs before it is completed.
}
timer.interval=60000 // 1 min
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
try
{
//Do something
}
catch
{
}
}
on backgroundworker completed event, just start background worker again
If your program is freezing, it may be because your infinitely looping background worker thread is spinning, using 100% CPU. You haven't said why you need it to run in an infinite loop, but you could start by putting a Thread.Sleep in that loop.
I'm trying to make it so that my form refreshes when I click a button. However I keep getting an error
'Cross-thread operation not valid: Control 'Form1' accessed from a
thread other than the thread it was created on.'
private void button1_Click(object sender, EventArgs e)
{
worker.DoWork += formReload;
worker.RunWorkerAsync();
}
static BackgroundWorker worker = new BackgroundWorker();
private void formReload(object sender, DoWorkEventArgs ev)
{
this.Refresh();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
}
I've tried to research it, and I get that I have to use the Invoke method, however I don't understand where to put it, and why to use it? Any help would be much appreciated!
Thanks,
Jarrod
Actually your code does nothing, the DoWork is unnecessary. You can rewrite your code as:
private void button1_Click(object sender, EventArgs e)
{
worker.RunWorkerAsync();
}
static BackgroundWorker worker = new BackgroundWorker();
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
if (this.InvokeRequired)
this.Invoke(new Action(()=>Refresh()));
}
Assuming that you subscribed the DoWork method in the contructor using
worker.DoWork += backgroundWorker1_DoWork;
Take note that a Refresh doesn't change anything. What do you have to refresh?
I have a function that adds a lot of files to a collection and does a lot of actions on each of them.
This causes the program (main thread) to become unresponsive.
How can I determine the cause and address the problem?
Use the BackgroundWorker object. You can inform the user with the progress of the operation by using the ReportProgress and ProgressChanged event.
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
private void Form1_Load(object sender, EventArgs e)
{
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// .. stuff that takes long
backgroundWorker1.ReportProgress(10);
// .. stuff that takes long
backgroundWorker1.ReportProgress(20);
// .. stuff that takes long
backgroundWorker1.ReportProgress(100);
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Done !
}
Use another thread.
Thread t = new Thread(new ThreadStart( () => {
IntensiveCalculationCode();
}));
t.Start();
You can also invoke the UI thread from inside the thread by invoking the dispatcher. They are not exactly the same on Windows Forms and WPF though.