i got some really simple code, but cant get it to work. I'm using BackgroundWorker. Problem is that RunWorkerCompleted is fired way to fast. Instantly after running i get message "Work completed", but application remains frozen for couple of seconds as 'DataType data = new DataType(path);' is beign executed. After that i got all my DataGridViews etc filled correctly. If i swap this single line with Thread.Sleep everything seems to work well. Any ideas?
public frmWindow(string path)
{
InitializeComponent();
DataType d;
backgroundWorker1.RunWorkerAsync(path);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
string path = e.Argument as string;
DataType data = new DataType(path);
e.Result = data;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
d = e.Result as DataType;
MessageBox.Show("Work completed");
}
How about you use Debug.Write instead of MessageBox.Show with timers to show when the Methods are entered and exited.
While it is possible for this same background thread to act on your UI, its almost always NOT a good thing to do--UI is not threadsafe.
BackgroundWorker backGroundWorker1;
public frmWindow(string path)
{
InitializeComponent();
DataType d;
backGroundWorker1 = new BackgroundWorker();
backGroundWorker1.DoWork += (s, e) =>
{
System.Diagnostics.Debug.Write("Work started at: " + DateTime.Now + Environment.NewLine);
string path = e.Argument as string;
DataType data = new DataType(path);
e.Result = data;
};
backGroundWorker1.RunWorkerCompleted += (s, e) =>
{
d = e.Result as DataType;
System.Diagnostics.Debug.Write("Work completed at: " + DateTime.Now + Environment.NewLine);
};
backGroundWorker1.RunWorkerAsync();
}
Related
first off I'd like to say I'm brand new to C# so I am not too aware with how the background worker is supposed to be implemented. I have a GUI program that basically pings a domain a returns the response to a textbox. I am able to get it to work normally, however, it freezes the code because it is running on the same thread which is why I am trying to implement a background worker.
Here is the basic setup
private void button1_Click(object sender, EventArgs e)
{
url = textBox1.Text;
button1.Enabled = false;
button2.Enabled = true;
bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.RunWorkerAsync();
}
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
do
{
if (bgWorker.CancellationPending)
break;
Invoke((MethodInvoker)delegate { monitor(); });
} while (true);
}
public void monitor()
{
textBox2.AppendText("Status of: " + url + "\n");
Status(url);
System.Threading.Thread.Sleep(30000);
}
private void Status(string url)
{
// This method does all the ping work and also appends the status to the Text box as it goes through , as OK or down
}
I have not worked with bgworkers before and as you can imagine it's confusing. I've looked at tons of other articles and I can't seem to get it. Sorry if the code looks crazy, I'm trying to learn.
Use Microsoft's Reactive Framework (NuGet "System.Reactive.Windows.Forms" and add using System.Reactive.Linq;) and then you can do this:
private void button1_Click(object sender, EventArgs e)
{
var url = textBox1.Text;
Observable
.Interval(TimeSpan.FromMinutes(0.5))
.SelectMany(_ => Observable.Start(() => Status(url)))
.ObserveOn(this)
.Subscribe(status => textBox2.AppendText("Status of: " + status + "\n"));
}
You then just need to change Status to have this signature: string Status(string url).
That's it. No background worker. No invoking. And Status is nicely run on a background thread.
You've got several mistakes. First,
Invoke((MethodInvoker)delegate
{
monitor();
});
will call monitor() on your UI thread. In almost all cases you should not call methods on other threads. You especially should not call methods that block or do anything that takes more than a few milliseconds on your UI thread, and that is what this does:
System.Threading.Thread.Sleep(30000);
Instead of calling a method on another thread; submit immutable data to the other thread and let the thread decide when to handle it. There is an event already built in to BackgroundWorker which does that. Before you call bgWorker.RunWorkerAsync() do this:
url = new Uri(something);
bgWorker.WorkerReportsProgress = true;
bgWorker.WorkerSupportsCancellation = true;
bgWorker.ProgressChanged += Bgw_ProgressChanged;
private void Bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
textBox2.AppendText("Status of: " + url + ": " + e.UserState.ToString()
+ Environment.NewLine);
}
Your bgWorker_DoWork should look more like this:
void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
while (!bgw.CancellationPending)
{
System.Threading.Thread.Sleep(new TimeSpan(0, 0, 30));
var status = ResultOfPing(e.Argument as Uri);
bgw.ReportProgress(0, status);
}
e.Cancel = true;
}
and you should call it like this:
bgWorker.RunWorkerAsync(url);
You've got a second problem. BackgroundWorker creates a thread, and your thread is going to spend most of its time blocked on a timer or waiting for network responses. That is a poor use of a thread. You would be better off using completion callbacks or async/await.
The background worker is running on a thread pool thread, but your call to Status and Sleep is running on the UI thread. You need to move that stuff back into bgWorker_DoWork.
Try this code:
public partial class Form1 : Form
{
bool cancel;
public Form1()
{
InitializeComponent();
}
public void StartPinging()
{
this.cancel = false;
startButton.Enabled = false;
stopButton.Enabled = true;
responseBox.Clear();
responseBox.AppendText("Starting to ping server.");
responseBox.AppendText(Environment.NewLine);
var bw = new BackgroundWorker
{
WorkerReportsProgress = false,
WorkerSupportsCancellation = true
};
bw.DoWork += (obj, ev) =>
{
while (!cancel)
{
// Ping Server Here
string response = Server.PingServer();
this.Invoke(new UiMethod(() =>
{
responseBox.AppendText(response);
responseBox.AppendText(Environment.NewLine);
}));
}
};
bw.RunWorkerCompleted += (obj, ev) =>
{
this.Invoke(new UiMethod(() =>
{
responseBox.AppendText("Stopped pinging the server.");
responseBox.AppendText(Environment.NewLine);
startButton.Enabled = true;
stopButton.Enabled = false;
}));
};
bw.RunWorkerAsync();
}
delegate void UiMethod();
private void startButton_Click(object sender, EventArgs e)
{
StartPinging();
}
private void stopButton_Click(object sender, EventArgs e)
{
responseBox.AppendText("Cancelation Pressed.");
responseBox.AppendText(Environment.NewLine);
cancel = true;
}
}
public class Server
{
static Random rng = new Random();
public static string PingServer()
{
int time = 1200 + rng.Next(2400);
Thread.Sleep(time);
return $"{time} ms";
}
}
Erwin, when dealing with C# - threads and UI elements usually you will come across cross-thread operations i.e. Background thread with UI threads. This interaction needs to be done in thread safe way with the help of Invoke to avoid invalid operations.
Please look into below resource: InvokeRequired section.
https://learn.microsoft.com/en-us/dotnet/framework/winforms/controls/how-to-make-thread-safe-calls-to-windows-forms-controls
I have a problem with background worker, it gets called twice thus, increasing the time of execution for my long routine, I created background worker manually so, there is no chance for the DoWork to be initialized within the initializeComponent() method, any help is appreciated.
here is my code:
// constructor
public TeacherScheduleForm(Therapist therapist)
{
this.therapist = therapist;
InitializeComponent();
bw = new BackgroundWorker();
bw.WorkerSupportsCancellation = true;
bw.WorkerReportsProgress = true;
bw.DoWork += bw_DoWork;
bw.ProgressChanged += bw_ProgressChanged;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
load = new LoadingForm();
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
load.AppendProgress(e.ProgressPercentage);
// load.AppendText(e.ProgressPercentage.ToString() + "%");
Console.Write("Progress: " + e.ProgressPercentage);
// MessageBox.Show("Progress : " + e.ProgressPercentage);
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((e.Cancelled == true))
{
MessageBox.Show("Cancelled");
}
else if (!(e.Error == null))
{
MessageBox.Show("Error : " + e.Error);
}
else
{
updateUI();
load.Close();
Console.Write( "Done!");
}
}
// do work of background worker
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; (i <= 2); i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
// Perform a time consuming operation and report progress.
Console.Write("Before Doing work");
setup(therapist.therapistID + "", schoolYear);// the time consuming operation
Console.Write("Doing work");
//System.Threading.Thread.Sleep(100);
worker.ReportProgress((i*5));
}
}
}
The background worker is called when the user selects the school year through a combo box which is in this code below:
private void comboBoxSchoolYear_SelectedIndexChanged(object sender, EventArgs e)
{
//load = new LoadingForm();
schoolYear = int.Parse(comboBoxSchoolYear.SelectedValue + "");
try{
if (!bw.IsBusy)
{
bw.RunWorkerAsync();
load.ShowDialog();
}
else
{
bw.CancelAsync();
}
}
catch(Exception ex)
{
Console.Write("Error : " + ex.Message);
}
}
You are loading the form after creating the event-handler. Thats the only point I can think off doing the trouble. Try to load the form first and then create the handler.
Reason: At InitializeComponent(); the IndexChanged normally will fire up because the control is set at this point with its index. I havnt noticed this behaviour on FormLoad till now. But as I cant see any other problem in here its worth a try.
IF this doesnt solves it, you should also take care if TeacherScheduleForm is being called twice.
Something handy for debugging-purposes:
MessageBox.Show((new StackTrace().GetFrame(0).GetMethod().Name));
Paste this into your event/method or whatever. It will popup a messagebox with the method-name which called your current method. In this case (from comments) it would've raised 2 messageBoxes saying TeacherScheduleForm for both.
I've saved this to my code-snippets.
I found a few examples on the internet but I'm having a hard time trying to run my async call inside a background worker. I'm working in WP8. What's missing?
Edit: The purpose of this is to update the values I'm reading from time to time. Let's say 1s.
Here is my async code, it just gets and stores some data in providers []:
public void getAllProvidersMethod()
{
try
{
sc.getAllProvidersCompleted += new EventHandler<ServiceReference1.getAllProvidersCompletedEventArgs>(callback);
sc.getAllProvidersAsync();
}
catch (System.Exception e)
{
MessageBox.Show(e.ToString());
}
}
public void callback(object sender, ServiceReference1.getAllProvidersCompletedEventArgs e)
{
try
{
providers = new String[e.Result.Length];
for (int i = 0; i < e.Result.Length; i++)
{
providers[i] = e.Result[i].ToString();
}
}
catch (System.Exception d)
{
MessageBox.Show(d.ToString());
}
}
This is my background worker:
private void networkWorker()
{
var obj = App.Current as App;
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(
delegate(object o, DoWorkEventArgs args)
{
BackgroundWorker b = o as BackgroundWorker;
getAllProvidersMethod();
});
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
delegate(object o, RunWorkerCompletedEventArgs args)
{
MessageBox.Show(" ");
});
bw.RunWorkerAsync();
}
Ok I managed to get it working!
For anyone that wants to run some async code automatically from time to time, forget about backgroundworkers, what you are looking for is a Timer.
Thank you Servy for the Timer hint.
Let's say I want to call asyncCall for each 5 seconds.
It's very simple. First define how long is your wait time between calls (how long you want it to wait before calling the same code again).
System.Windows.Threading.DispatcherTimer dt = new System.Windows.Threading.DispatcherTimer();
dt.Interval = new TimeSpan(0, 0, 0, 0, 5000); // HERE - for example 5000 is 5 seconds
dt.Tick += new EventHandler(dt_Tick);
dt.Start();
And then all you need is to write the code you want to repeat inside this method:
void dt_Tick(object sender, EventArgs e)
{
asyncCall();
}
In this case for each 5 seconds it will call asyncCall.
In my app I want create thousands files in background, but when I execute this it always freeze my form(after all files were created I can work with my form again). How run it properly without hanging?
private void button5_Click(object sender, EventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += new DoWorkEventHandler(
delegate(object o, DoWorkEventArgs args)
{
BackgroundWorker b = o as BackgroundWorker;
this.Invoke(new MethodInvoker(delegate
{
getValues();//load some text fields into strings
while (counter < counted)
{
text = richTextBox1.Text;
text = text.Replace("number", finalNumber);
//create copies
if (checkBox1.Checked == true)
{
while (createdCopies < copies)
{
createdCopies++;
File.WriteAllText(fileName, text);
overalCounter++;
b.ReportProgress(overalCounter);
}
counter++;
createdCopies = 0;
}
//dont create copies
else
{
File.WriteAllText(fileName, text);
counter++;
overalCounter++;
b.ReportProgress(overalCounter);
}
//info about number of files created
label6.Text = "created " + overalCounter.ToString() + " files";
}
label1.Text = "success";
}));
});
if (bw.IsBusy != true)
{
bw.RunWorkerAsync();
}
bw.ProgressChanged += new ProgressChangedEventHandler(
delegate(object o, ProgressChangedEventArgs args)
{
this.Text = string.Format("{0}% Completed", args.ProgressPercentage);
});
}
this.Invoke runs the code inside on the UI thread, blocking any UI updates.
Since you run everything in the Invoke method everything will run on the UI thread.
Create a separate Invoke around each place where you modify your UI controls and leave the heavy work outside the Invokes.
Do the actual work in your first delegate. The second delegate this.Invoke(...) executes on the thread of the form (=main thread) and therefore blocks you UI.
I'm stuck trying to update a progressbar from other threads ran in a different class. To explain what I do I think a picture will be better. I want to update the progressbar in the //HERE point :
I've tried using a delegate, tried with ReportProgress and I think i've basically tried to use everything google reported in the first 100 results, without success. I'm still learning WPF and this might be silly way to proceed, i'm looking for a quick and dirty way to get the work done but feel free to tell me what I should redesign for a cleaner application.
EDIT : More code.
In ExecutorWindow.xaml.cs :
public void RunExecutor()
{
// CREATE BACKGROUNDWORKER FOR EXECUTOR
execBackground.DoWork += new DoWorkEventHandler(execBackground_DoWork);
execBackground.RunWorkerCompleted += new RunWorkerCompletedEventHandler(execBackground_RunWorkerCompleted);
execBackground.ProgressChanged += new ProgressChangedEventHandler(execBackground_ProgressChanged);
execBackground.WorkerReportsProgress = true;
execBackground.WorkerSupportsCancellation = true;
// RUN BACKGROUNDWORKER
execBackground.RunWorkerAsync();
}
private void execBackground_DoWork(object sender, DoWorkEventArgs e)
{
myExecutor = new Executor(arg1, arg2);
myExecutor.Run();
}
private void execBackground_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("RunWorkerCompleted execBackground");
}
private void execBackground_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
ExecutorProgressBar.Value += 1;
}
// TESTING
private void updateProgressBar(int i)
{
ExecutorProgressBar.Value += i;
}
public delegate void callback_updateProgressBar(int i);
In Executor.cs :
public void Run()
{
string[] options = new string[2];
int i = 0;
while (LeftToRun > 0)
{
if (CurrentRunningThreads < MaxThreadsRunning)
{
BackgroundWorker myThread = new BackgroundWorker();
myThread.DoWork += new DoWorkEventHandler(backgroundWorkerRemoteProcess_DoWork);
myThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorkerRemoteProcess_RunWorkerCompleted);
myThread.ProgressChanged += new ProgressChangedEventHandler(backgroundWorkerRemoteProcess_ProgressChanged);
myThread.WorkerReportsProgress = true;
myThread.WorkerSupportsCancellation = true;
myThread.RunWorkerAsync(new string[2] {opt1, opt2});
// HERE ?
CurrentRunningThreads++;
i++;
LeftToRun--;
}
}
while (CurrentRunningThreads > 0) { }
logfile.Close();
MessageBox.Show("All Tasks finished");
}
private void backgroundWorkerRemoteProcess_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker myBackgroundWorker = sender as BackgroundWorker;
string[] options = (string[])e.Argument;
string machine = options[0];
string script = options[1];
// UPDATE HERE PROGRESSBAR ?
RemoteProcess myRemoteProcess = new RemoteProcess(machine, script);
string output = myRemoteProcess.TrueExec();
// UPDATE HERE PROGRESSBAR ?
this.logfile.WriteLine(output);
}
private void backgroundWorkerRemoteProcess_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
CurrentRunningThreads--;
}
private void backgroundWorkerRemoteProcess_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//myExecWindow.ExecutorProgressBar.Value = e.ProgressPercentage; // TESTING
//ExecutorWindow.callback_updateProgressBar(1); // TESTING
}
EDIT 2 : I got it! Simple in fact, but i guess I've been looking too close to find out.
In my ExecutorWindow class :
private void execBackground_DoWork(object sender, DoWorkEventArgs e)
{
myExecutor = new Executor(arg1, arg2);
myExecutor.Run(sender);
}
private void execBackground_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
ExecutorProgressBar.Value += 1;
}
And in my Executor class :
private BackgroundWorker myExecutorWindow;
[...]
public void Run(object sender)
{
myExecutorWindow = sender as BackgroundWorker;
string[] options = new string[2];
int i = 0;
while (LeftToRun > 0)
{
if (CurrentRunningThreads < MaxThreadsRunning)
{
BackgroundWorker myThread = new BackgroundWorker();
myThread.DoWork += new DoWorkEventHandler(backgroundWorkerRemoteProcess_DoWork);
myThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorkerRemoteProcess_RunWorkerCompleted);
myThread.ProgressChanged += new ProgressChangedEventHandler(backgroundWorkerRemoteProcess_ProgressChanged);
myThread.WorkerReportsProgress = true;
myThread.WorkerSupportsCancellation = true;
myThread.RunWorkerAsync(new string[2] {opt1, opt2});
CurrentRunningThreads++;
i++;
LeftToRun--;
}
}
[...]
private void backgroundWorkerRemoteProcess_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker myBackgroundWorker = sender as BackgroundWorker;
myBackgroundWorker.ReportProgress(1);
// PROCESSING MY STUFF HERE
myBackgroundWorker.ReportProgress(1);
}
private void backgroundWorkerRemoteProcess_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
myExecutorWindow.ReportProgress(1);
}
Thank you !
You can run any method on the UI thread with this very basic sample
this.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate()
{
this.progressBar.Value= 20; // Do all the ui thread updates here
}));
Running commands inside the Dispatcher.Invoke(...), you can actually interact with the UI from any worker thread, where otherwise you would get an exception.
If you really need to have the ultimate control on the background threads & main (UI) thread updates, here is a fantastic tutorial on that: http://blog.decarufel.net/2009/03/good-practice-to-use-dispatcher-in-wpf.html
You should be able to use the Dispatcher.Invoke method
e.g.
Dispatcher.Invoke(
new System.Action(() => myProgressBar.Value = newValue)
);
I got it! Simple in fact, but i guess I've been looking too close to find out.
In my ExecutorWindow class :
private void execBackground_DoWork(object sender, DoWorkEventArgs e)
{
myExecutor = new Executor(arg1, arg2);
myExecutor.Run(sender);
}
private void execBackground_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
ExecutorProgressBar.Value += 1;
}
And in my Executor class :
private BackgroundWorker myExecutorWindow;
[...]
public void Run(object sender)
{
myExecutorWindow = sender as BackgroundWorker;
string[] options = new string[2];
int i = 0;
while (LeftToRun > 0)
{
if (CurrentRunningThreads < MaxThreadsRunning)
{
BackgroundWorker myThread = new BackgroundWorker();
myThread.DoWork += new DoWorkEventHandler(backgroundWorkerRemoteProcess_DoWork);
myThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorkerRemoteProcess_RunWorkerCompleted);
myThread.ProgressChanged += new ProgressChangedEventHandler(backgroundWorkerRemoteProcess_ProgressChanged);
myThread.WorkerReportsProgress = true;
myThread.WorkerSupportsCancellation = true;
myThread.RunWorkerAsync(new string[2] {opt1, opt2});
CurrentRunningThreads++;
i++;
LeftToRun--;
}
}
[...]
private void backgroundWorkerRemoteProcess_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker myBackgroundWorker = sender as BackgroundWorker;
myBackgroundWorker.ReportProgress(1);
// PROCESSING MY STUFF HERE
myBackgroundWorker.ReportProgress(1);
}
private void backgroundWorkerRemoteProcess_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
myExecutorWindow.ReportProgress(1);
}
I found a really simple solution to create a thread to run any block of code as well as handle Invocation back to the main thread to change the control's properties. It works out of the box with .NET 4.5 and the lambda call on the Dispatcher could be adapted to work with earlier versions of .NET. The main benefit is it's just so blissfully simple and perfect when you just need a quick thread for some really basic bit of code.
So presuming you have a progress bar somewhere on your dialog in scope do this:
progBar.Minimum = 0;
progBar.Maximum = theMaxValue;
progBar.Value = 0;
Dispatcher disp = Dispatcher.CurrentDispatcher;
new Thread(() => {
// Code executing in other thread
while (progBar.Value < theMaxValue)
{
// Your application logic here
// Invoke Main Thread UI updates
disp.Invoke(
() =>
{
progBar.Value++;
}
);
}
}).Start();
You also need to ensure you have a reference to WindowsBase.dll
If you want a more reusable snippet of code running as the thread start you could use a method as the delegate but I find the inline lambda so easy for simple tasks and you don't need to deal with events as with the Background Worker approaches.