I've built a C# Winforms app that serves as a simulation to run a set of tasks that are computationally and memory intensive. I'm using a background worker to do the work so the UI can remain responsive. I've got just a for loop:
var iterations = Convert.ToInt32(txtNumIterations.Text);
for (int i = 0; i < iterations; i++)
{
ResetSim();
StartWorker("RunSimulation", i + 1);
}
What I would like is to just run these sequentially in the background, but I can't figure out if a background worker set up will do this, or if I need to use Tasks. I haven't found a really good example that explains how a Task could accomplish my goal. Any help would be appreciated.
You can use the BackgroundWorker.ReportProgress Method in the DoWork method in order to pass back information to the main thread. You could do the loop in the DoWork method and at each loop call worker.ReportProgress(i); with the loop variable. The value is not interpreted by the background worker, so it needs not to be a percentage value. Then in the ProgressChanged event handler you can call ResetSim();. (I assume that ResetSim acts in the UI and must therefore be executed on the UI thread.)
If you need to run your simulations sequentially then you can subscribe to the background workers RunWorkerCompleted event to start the next one.
So you just start the first worker off:
var worker1 = new BackgroundWorker();
worker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
worker1.RunWorkerAsync();
Then in the handler:
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
var worker2 = new BackgroundWorker();
worker2.RunWorkerCompleted += backgroundWorker2_RunWorkerCompleted;
worker2.RunWorkerAsync();
}
Obviously you can use an array to hold the references to each worker and have a class variable to hold which one's active. You'd then only need one handler to increment the simulation count and start the next one in the list.
From your description it sounds like you really don't need multiple background workers, just use a single background worker and run the loop inside of it.
Related
so when i try and press "button 2" I expect two things to happen a)"dowsomething" is suppose to do its thing in the "now" class. b) Whilst its doing something i want it to count how long that something takes. However because "dosomething" is program hungry Form1 freezes and it wont run the timer. Im a bit of a rookie at c# so I wouldn't know how to run it in the background. So any outside the box ideas? Thanks.
int time = 0;
private void button2_Click(object sender, EventArgs e)
{
timer1.Start();
nowthen now = new nowthen();
now.dosomething(withthis); //This task is program hungry and causes the form to freeze
timer1.Stop();
time = 0;
}
private void timer1_Tick(object sender, EventArgs e)
{
time = time + 1;
label2.Text = time.ToString();
label2.Refresh();
}
In Windows Forms, all of your UI stuff runs on one thread. That includes the timer - the timer is implemented behind the scenes with windows messages.
Your question is actually two questions:-
How can I time an operation in C# / Windows forms?
How to time something depends on the precision you're looking for. For accuracy in the region of +/- 10ms then you can use Environment.TickCount - store it's value before your operation, then get the value again after, and subtract the stored value - and you have your duration.
More precise is the Stopwatch class in System.Threading - see http://www.dotnetperls.com/stopwatch
How can I run a task "in the background" ?
To run your operation in the background, you need to run it in a different thread. The easiest, designed friendly (but perhaps not all that flexible way) is to use the BackgroundWorker component. This wraps using a worker thread to do an operation for you. See http://www.dotnetperls.com/backgroundworker for a good explanation of how to do that.
More advanced, and more flexible, is to create your own thread to do the work. However, that will create some important issues to consider around how to syncronize what's going on - as soon as you start your thread, your method call finishes (it's asyncronous) and you need to have a mechanism for notifiying your UI code that the process has finished. This example seems as good as any on how to create your own thread: http://www.daveoncsharp.com/2009/09/create-a-worker-thread-for-your-windows-form-in-csharp/
For .NET 4 use:
Task.Factory.StartNew((Action) delegate()
{
// this code is now executing on a new thread.
nowthen now = new nowthen();
now.dosomething(withthis);
// to update the UI from here, you must use Invoke to make the call on UI thread
textBox1.Invoke((Action) delegate()
{
textBox1.Text = "This update occurs on the UI thread";
});
});
If you just want to time how long something takes, use System.Diagnostics.Stopwatch.
Stopwatch sw = Stopwatch.StartNew();
nowThen = new nowThen();
no.dosomething(withthis);
sw.Stop();
// you can get the time it took from sw.Elapsed
That won't, however, update a label with the elapsed time.
I guess I'll throw this in too, although it's not as elegant looking as #paul's solution.
timer1.Start();
var bw = new BackgroundWorker();
bw.DoWork += (s, e) => { now.dosomething((myArgumentType)e.Argument); };
bw.RunWorkerCompleted += (s, e) => { timer1.Stop(); };
bw.RunWorkerAsync(withthis);
This starts your timer, creates a new BackgroundWorker thread, tells it what to run in the DoWork method (dosomething runs in a separate thread), then stops the timer in the RunWorkerCompleted method (after dosomething is finished, control returns to the main thread in RunWorkerCompleted).
I came across with a little problem. I have two threads one that executes a loop that needs to return/send a number to the GUI's thread every time. For this I use the BackGroundWorker and the ReportProgress .
Let say something like that:
I have a BackGroundWorker that executes (DoWork) a simple loop that counts from 0 to whatever. Every entry to the loop I send the counter using the ReportProgress event to the GUI's thread that will print the counter's value.
void worker_DoWork(object sender, DoWorkEventArgs e)
{
int count = 0;
BackgroundWorker Worker = (BackgroundWorker)sender;
while (count < 10000000)
{
Worker.ReportProgress(count);
count++;
}
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
txt.Text = e.ProgressPercentage.ToString();
}
Now, this opretion freezes the GUI.
I understand that the ReportProgress is invoking the ProgressChange handler at the thread that created the BackGroundWorker , So I think the loop is executeing so fast so the GUI's thread isn't succeeding to print the values as required.
What Can I do to perform a task like that without freezing the GUI?
I heard of the Dispatcher but I don't really sure for what it uses for.
The problem is that you are calling reportProgress every time something changes. You should call it only when you "need" to report progress. See MSDN http://msdn.microsoft.com/en-us/library/ka89zff4.aspx.
Change your dowork to something like this:
while (count < 10000000)
{
if ((count % 1000) == 0)
Worker.ReportProgress(count);
count++;
}
This will call ReportProgress after each 1000 processed items and therefore not put unnecessary load to your GUI thread
You example code is attempting to update the GUI at a much faster rate than the GUI can process the update notification messages, so flooding the GUI Windows message queue with gunge and preventing it handling other messages - GUI freeze.
Monitoring the progress of high-rate operations in a non-GUI thread is one of the few times when polling is the better solution. Use a Forms.Timer event to read and display a 'currentProgress' value, perhaps returned by a method of the thread. 500ms is a reasonable timer value - human users cannot keep up with a changing integer value in an edit/text box at a rate much faster than that.
'Ideally', the read/write of the currentProgress value should be locked up, perhaps with an atomic op, but if you're only reading an int every 500ms, you probably don't even need that if the 'real' functionality of the thread means that the progress count is very unlikely to be continuously cached in a register.
What Can I do to perform a task like that without freezing the GUID? :
Using dispatcher made me assume you're using WPF, anyways, it would be :
void worker_DoWork(object sender, DoWorkEventArgs e)
{
int count = 0;
BackgroundWorker Worker = (BackgroundWorker)sender;
while (count < 10000000)
{
Worker.ReportProgress(count);
count++;
}
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
Dispatcher.BeginInvoke((Action)(() => { txt.Text = e.ProgressPercentage.ToString(); }));
}
Calling the Dispatcher.BeginInvoke causes the given Action to actually be executed on the UI thread, making sure no Exception is thrown for the cause of a thread other than the UI thread accessing a UI element.
Also, you might try this, just as an alternative by using a task.
I want to run two functions simultaneously.
The first function will count the total number of files in my computer and will show the result in one DatagridView.
And in the second function, one progress bar's value will be changed according to the total number of files, meaning that when all files are bound in the DatagridView, the progress bar's value will be set to 100.
I want to synchronize the two functions' work, I am using two threads but the result does not show, an exception occurred.
What should I do ?
my code is
ThreadStart threadstatr = new ThreadStart(Function_1);
ThreadStart threaProgress = new ThreadStart(Function_2);
Thread thread = new Thread(threadstatr);
Thread threadpro = new Thread(threaProgress);
thread.Start();
threadpro.Start();
thread.Join();
threadpro.Join();
void Function_2()
{
int coun = ((Convert.ToInt32(label1.Text)) / 100); //here label1 represents total no. of files
for (int i = 0; i < coun; i++)
{
backgroundWorker1.ReportProgress(i);
}
}
and exception is "Input string was not in a correct format." as the label does not get chance to update itself
Hmm its seems a little more complicated than it needs to be. Using BackgroundWorker should enable you to avoid raw Thread objects (ThreadStart etc) completely.
You have a Main app / UI thread where your progress bar is instantiated.
You want to use a BackgroundWorker to spin up a thread to do the work - by hooking up your work (search files) method to the BackgroundWorker.DoWork event.
That method can report progress back to the UI thread using BackgroundWorker.ReportProgress method.
Your UI thread can react to progress (increment progress control) by handling BackgroundWorker.ProgressChanged event.
Dont forget to set BackgroundWorker.WorkerReportsProgress = true when you create your BackgroundWorker.
You can get loads more detail on this by looking at doc for BackgoundWorker.
Not to repeat another answer and my comment but you don't seem to be listening.
Drop the threads. No ThreadStart in your code.
And don't read a UI control from a the background.
Pass data:
backgroundWorker1.RunWorkerAsync(numberToCompute);
Use BackgroundWorker.
This does exactly what you need if you will follow.
BackgroundWorker Class
Suppose you have a search textbox and have a search algorithm attached to the TextChanged event, that runs with a BackgroundWorker. If there comes a new character in the textbox, i need to cancel the previous search and run it again.
I tried using events in between the main thread and the bgw, from this previous question, but I still get the error "currently busy and cannot run multiple tasks concurrently"
BackgroundWorker bgw_Search = new BackgroundWorker();
bgw_Search.DoWork += new DoWorkEventHandler(bgw_Search_DoWork);
private AutoResetEvent _resetEvent = new AutoResetEvent(false);
private void txtSearch_TextChanged(object sender, EventArgs e)
{
SearchWithBgw();
}
private void SearchWithBgw()
{
// cancel previous search
if (bgw_Search.IsBusy)
{
bgw_Search.CancelAsync();
// wait for the bgw to finish, so it can be reused.
_resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
}
// start new search
bgw_Search.RunWorkerAsync(); // error "cannot run multiple tasks concurrently"
}
void bgw_Search_DoWork(object sender, DoWorkEventArgs e)
{
Search(txtSearch.Text, e);
}
private void Search(string aQuery, DoWorkEventArgs e)
{
int i = 1;
while (i < 3) // simulating search processing...
{
Thread.Sleep(1000);
i++;
if (bgw_Search.CancellationPending)
{
_resetEvent.Set(); // signal that worker is done
e.Cancel = true;
return;
}
}
}
EDIT To reflect answers. DonĀ“t reuse the BackgroundWorker, create a new one:
private void SearchWithBgw()
{
if (bgw_Search.IsBusy)
{
bgw_Search.CancelAsync();
_resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
bgw_Search = new BackgroundWorker();
bgw_Search.WorkerSupportsCancellation = true;
bgw_Search.DoWork += new DoWorkEventHandler(bgw_Search_DoWork);
}
bgw_Search.RunWorkerAsync();
}
When the _resetEvent.WaitOne() call completes, the worker thread isn't actually done. It is busy returning from DoWork() and waiting for an opportunity to run the RunWorkerCompleted event, if any. That takes time.
There is no reliable way to ensure the BGW is completed in a synchronous way. Blocking on IsBusy or waiting for the RunWorkerCompleted event to run is going to cause deadlock. If you really want to use only one bgw then you'll have to queue the requests. Or just don't sweat the small stuff and allocate another bgw. They cost very little.
Create a new background worker if the old one exists.
private void SearchWithBgw()
{
// cancel previous search
if (bgw_Search.IsBusy)
{
bgw_Search.CancelAsync();
// wait for the bgw to finish, so it can be reused.
_resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
BackgroundWorker bgw_Search = new BackgroundWorker();
bgw_Search.DoWork += new DoWorkEventHandler(bgw_Search_DoWork);
}
// start new search
bgw_Search.RunWorkerAsync(); // error "cannot run multiple tasks concurrently"
}
Also I know you put fake code in, but you want to make sure you set _resetEvent when the code completes normally too.
Do not reuse a Backgroundworker. It is a cheap resource, it is not a Thread.
make sure your Bgw code stops, yours looks OK. The Bgw will release the Thread to the pool.
but in the mean time, create a new Task/Bgw for a new job.
You may want to unsubscribe your Completed event from the old Bgw.
I think you should consider not cancelling the background worker.
If you cancel requests and the user types faster than your server returns queries, he will not see suggestions until he is finished typing.
In interactive scenarios like this, It could be better to show responses that run behind with what the user's typing. Your user will know he can stop typing if the word he has in mind is your suggestions list.
This will be also better for your server when it is busy, because instead of many cancelled requests, who will cost something but that are ultimately not shown, there will be fewer requests whose response you actually use.
I ran into similar issues with (3d) rendering applications, where the beginner's mistake is to cancel and rerender on every mousemove. This lead to a lot of computation and little interactive feedback.
this is a snippet from my class:
public bool start()
{
Thread startThread = new Thread(this.ThreadDealer);
startThread.Start();
return _start;
}
In ThreadDealer() I'm setting the boolean variable "_start" to false or true. What I need now but can't seem to figure out is an event to alert start() to execute its return statement when the ThreadDealer()-Thread has finished.
I tried something with an AutoResetEvent and .WaitOne() but since I have a GUI that just blocks everything and while it does what I need it to do (wait for the Thread to finish) it is useless if it blocks my GUI.
Any help would be much appreciated.
What you want to do -- wait for the background thread in a method of the UI thread but still allow the UI to be responsive -- is not possible. You need to split your code into two parts: One executed before starting (or parallel to) the background thread and the other one running after the background thread has finished.
The easiest way is to use the BackgroundWorker class. It raises an event in the UI thread (RunWorkerCompleted) after its work is done. Here's an example:
public void start()
{
var bw = new BackgroundWorker();
// define the event handlers
bw.DoWork += (sender, args) => {
// do your lengthy stuff here -- this will happen in a separate thread
...
};
bw.RunWorkerCompleted += (sender, args) => {
if (args.Error != null) // if an exception occurred during DoWork,
MessageBox.Show(args.Error.ToString()); // do your error handling here
// Do whatever else you want to do after the work completed.
// This happens in the main UI thread.
...
};
bw.RunWorkerAsync(); // starts the background worker
// execution continues here in parallel to the background worker
}
Just raise an event. It will run on the wrong thread so whatever event handler has to deal with that by marshaling the call if necessary to update any UI. By using Control.Begin/Invoke or Dispatcher.Begin/Invoke, depending what class library you use.
Or use the BackgroundWorker class, it does it automatically.