I've been trying to get the logic right for my timer and backgroundworker thread. Granted I don't fully understand the whole system despite all my reading. the following are excerpts of code concerned:
My polling button :
private void pollStart_Click(object sender, EventArgs e)
{
tst_bgw = new BackgroundWorker();
//mandatory. Otherwise will throw an exception when calling ReportProgress method
tst_bgw.WorkerReportsProgress = true;
//mandatory. Otherwise we would get an InvalidOperationException when trying to cancel the operation
tst_bgw.WorkerSupportsCancellation = true;
tst_bgw.DoWork += tst_bgw_DoWork;
tst_bgw.ProgressChanged += tst_bgw_ProgressChanged;
tst_bgw.RunWorkerCompleted += tst_bgw_RunWorkerCompleted;
tst_bgw.RunWorkerAsync();
}
which I think is right so far
my Background worker thread:
private void tst_bgw_DoWork(object source, DoWorkEventArgs e)
{
m_timer = new System.Timers.Timer();
m_timer.Interval = 1000;
m_timer.Enabled = true;
m_timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
if (tst_bgw.CancellationPending)
{
e.Cancel = true;
return;
}
}
and the elapsed tier event code:
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
if (powerVal > 3250)
{
m_timer.Stop();
tst_bgw.CancelAsync();
}
else
{
string pow;
int progressVal = 100 - ((3250 - powerVal) / timerVal);
uiDelegateTest tstDel = new uiDelegateTest(recvMessage);// the recvMessage function takes a textbox as an argument and directs output from socket to it.
pow = construct_command("power", powerVal);
sData = Encoding.ASCII.GetBytes(pow);
if (active_connection)
try
{
m_sock.Send(sData);
Array.Clear(sData, 0, sData.Length);
tstDel(ref unit_Output);// Read somewhere that you can only modify UI elements in this method via delegate so I think this is OK.
m_sock.Send(time_out_command);
tstDel(ref unit_Output);
tst_bgw.ReportProgress(progressVal);
}
catch (SocketException se)
{
MessageBox.Show(se.Message);
}
tst_bgw.ReportProgress(powerVal, progressVal);
powerVal = powerVal + pwrIncVal;
}
I'd just like to know a few other things; am I using the right timer (not that I think it should matter greatly but it was suggested that this might be the best timer for what I want to do) and canI really modify UI elements in the DoWork method only through delegates and if yes are there sepcial considerations to doing so.
Sorry about the long posting and thank you for your time.
There is lots wrong with this code.
1) You aren't disposing of your background worker. BackgroundWorkers must be disposed of after use. They are designed to be used as winforms components and would normally be added to a window via the designer. This will ensure it is created with the form and disposed of when the form is.
2) All you are doing in your dowork method is creating a new timer and running it. There is no point of doing this in a background worker because it will happen so quickly anyway.
3) You will recreate the timer every time you run the background worker again. But you aren't ever stopping or disposing of the old timer, you are just overwriting the member.
I recommend you get rid of the BackgroundWorker completely and just use a timer. Create the timer in the forms constructor and make sure you dispose of it in the forms dispose method. (Or use the designer to add it to the form). In the pollstart_click method just start the timer. (If you have a poll stop method, you can stop the timer in that)
You don't need both a BackgroundWorker and a Timer to accomplish your goal. From what you have posted it looks like you want to have the user click a button which starts a polling process that quits at a certian point.
Your polling model really suggests a timer would work just fine.
If you use a Timer I would Initialize the timer after the InitializeComponent() call with something like
private void InitializeTimer()
{
this.timer = new Timer();
int seconds = 1;
this.timer.Interval = 1000 * seconds; // 1000 * n where n == seconds
this.timer.Tick += new EventHandler(timer_Tick);
// don't start timer until user clicks Start
}
The button_click will simply
private void button_Click(object sender, EventArgs e)
{
this.timer.Start();
}
Then on the timer_Tick you will need to do your polling and you should be able to update your UI from there if the timer is on the UI thread like this
void timer_Tick(object sender, EventArgs e)
{
if( determineIfTimerShouldStop() )
{
this.timer.Stop();
}
else
{
// write a method to just get the power value from your socket
int powerValue = getPowerValue();
// set progressbar, label, etc with value from method above
}
}
However if the timer thread is not on the same thread as the UI you well get an exception while trying to update the UI. In that case you can use the Invoke that DataDink mentions and do something like this
void timer_Tick(object sender, EventArgs e)
{
if( determineIfTimerShouldStop() )
{
this.timer.Stop();
}
else
{
// write a method to just get the power value from your socket
int powerValue = getPowerValue();
// set a label with Invoke
mylabel.Invoke(
new MethodInvoker( delegate { mylabel.Text = "some string"; } )
);
}
}
Given the code you posted you really didn't need to do both a BackgroundWorker and a Timer, but I have had instances where I have used a BackgroundWorker to do work when a timer is called so that I could have a timer update UI periodically and have a manual button to Refresh the UI. But I wasn't updating my UI quite the way you are.
If you still have the need to do both, here is, roughly, how you can flow your app...
Create an
InitailizeBackgroundWorker() method
along with the InitializeTimer so you have
it already initalized before the
Timer fires.
Then set the Timer.Tick
to call the
BackgroundWorker.RunWorkerAsync()
Then you can do all the UI updates from within the RunWorkerAsync by
using the
BackgroundWorker.ReportProgress().
Related
I have an app which uses DispatcherTimer to manage time, countdown things etc. and I do have multiple counters turned on while app is open. But the time is a little bit delayed, I'd say about 3-5 seconds per minute. This is part of the code I'm using:
DispatcherTimer ob = new DispatcherTimer();
ob.Interval = new TimeSpan(0, 0, 1);
private void bob_Click(object sender, RoutedEventArgs e) //Button starting countdown
{
ob.Start();
tikOb = 140;
ob.Tick += new EventHandler(ob_Tick);
}
void ob_Tick(object sender, EventArgs e)
{
tob.Text = tikOb.ToString();
if (tikOb > 0)
{
tikOb--;
}
else
{
ob.Stop();
tob.Text = "STOPPED";
ob.Tick -= new EventHandler(ob_Tick);
}
//Between these there is a code which is irrelevant in this case.
private void stopob_Click(object sender, RoutedEventArgs e) //Button breaking countdown
{
ob.Tick -= new EventHandler(ob_Tick);
ob.Stop();
tob.Text = "ON";
{
Can anyone tell me why is this happening? Did I do anything wrong inside the code? Oh, I also have another one in the code which uses different variables etc. It's completely separated. Thanks in advance!
The DispatcherTimer is executed on the UI thread. If the UI thread is currently busy doing other work for more than the interval, it will delay the execution of the Timer event and invoke it once it is free of prior work. If you need more precise scheduling, you should go for a time than runs in the background, like System.Threading.Timer or Task.Delay which can be awaited, if you're using .NET 4.5 and above. If you use the Timer and then invoke UI thread logic, you will have to remember to marshal back work to the UI thread.
I've looked at some guides and none of them have gotten me all the way there. I've never made a thread, discussed a thread, or seen a thread at the grocery store, so this may be a problem. Currently. I'm trying:
private void btnHUp_MouseDown(object sender, MouseEventArgs e)
{
{
ThreadStart HUp = new ThreadStart(dothis);
t = new Thread(HUp);
t.Start();
}
}
public void dothis()
{
if (intHour < 23)
intHour = intHour += intStep;
lblTimerHour.Text = intHour.ToString("00");
}
private void btnHUp_MouseUp(object sender, MouseEventArgs e)
{
t.Abort();
}
}
That gets me InvalidOperationException was unhandled on the
lblTimerHour.Text = intHour.ToString("00");
line. I read what that means and... it might as well be in Mandarin, I kind of get the general concept-ish of what's going wrong, but it's painfully fuzzy. If you asked me the first step in fixing it I'd look at you like a deer in the headlights. We just haven't gotten that far in my class yet.
The problem here is that the label you are trying to update is owned by the main thread (i.e. what the UI runs on), and that means that only that thread can access/update it. So, since you are in a different thread, you need to tell the UI thread to update the label for you.
Something like this would work:
Action updateLabel = () => lblTimerHour.Text = intHour.ToString("00");
lblTimerHour.BeginInvoke(updateLabel);
What this does is tell the lblTimerHour to invoke the action you define above (updateLabel).
See this post: How to update the GUI from another thread in C#?
lblTimerHour.Invoke((MethodInvoker)delegate {
//Do what you need to do with the label
lblTimerHour.Text = intHour.ToString("00");
});
Edit
This should do the trick:
public void dothis()
{
do
{
if (intHour < 23)
intHour = intHour += intStep;
lblTimerHour.Invoke((MethodInvoker)delegate {
//Update the label from the GUI thread
lblTimerHour.Text = intHour.ToString("00");
});
//Pause 1 sec. Won't freeze the gui since it's in another thread
System.Thread.Sleep(1000);
}while(true); //Thread is killed on mouse up
}
Well, let's take a look and see what you already have.
First, I see you did this.
private void btnHUp_MouseDown(object sender, MouseEventArgs e)
{
ThreadStart HUp = new ThreadStart(dothis);
t = new Thread(HUp);
t.Start();
}
While this certainly is not the freshest stuff around it will still work. If you wanted some fresher ingredients then you might go with this instead.
private void btnHUp_MouseDown(object sender, MouseEventArgs e)
{
Task.Factory.StartNew(dothis);
}
Second, I see this.
public void dothis()
{
if (intHour < 23) intHour = intHour += intStep;
lblTimerHour.Text = intHour.ToString("00");
}
The problem here is that you are attempting to update a UI control from a thread other than the main UI thread. You see UI controls have what is called thread affinity. They can only ever be accessed from the thread that created them. What you have will lead to all kinds of unpredictable problems up to and including tearing a whole in spacetime.
A better option would be to do this.
public void dothis()
{
while (intHour < 23)
{
intHour = intHour += intStep;
lblTimerHour.Invoke((Action)(
() =>
{
lblTimerHour.Text = intHour.ToString("00");
}));
}
}
I assumed that you were missing the loop so I added it. While I cannot say that I personally have a taste for this kind of thing it is much easier to swallow. The real problem here is that the worker thread really does not do a whole lot of useful work. And then to top it off we have to use an awkward marshaling operation to transfer the result back to the UI thread. It is not pretty, but it will work.
And finally that brings me to this.
private void btnHUp_MouseUp(object sender, MouseEventArgs e)
{
t.Abort();
}
You are attempting to abort a thread which is highly inadvisable. The problem is that it yanks control from the thread at unpredictable times. That thread might be in the middle of a write to data structure which would corrupt it. This is actually a pretty bad problem because any data structure in the process of being manipulated from any one of the frames on the call stack could be in an inconsistent state. This includes code you did not write. That is why it is hard to say what you may or may not be corrupting by doing this.
What you need to consider instead is using the cooperative cancellation mechanisms. This includes the use of CancellationTokenSource and CancellationToken. Here is how it might look once we put everything together.
private CancellationTokenSource cts = null;
private void btnHUp_MouseDown(object sender, MouseEventArgs e)
{
cts = new CancellationTokenSource();
Task.Factory.StartNew(() => dothis(cts.Token));
}
private void btnHUp_MouseUp(object sender, MouseEventArgs e)
{
cts.Cancel();
}
public void dothis(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
intHour += intStep;
lblTimerHour.Invoke((Action)(
() =>
{
lblTimerHour.Text = intHour.ToString("00");
}));
Thread.Sleep(1000);
}
}
What this does is signal that the worker thread should gracefully shutdown on its own. This gives the worker thread a chance to tidy things up before eventually terminating itself.
If you want to update the UI every X period of time then there are already existing tools for this; a Timer will do exactly what you want, and it will be much more efficient and easier to code than creating a new thread that just spends most of its time napping. Additionally, aborting threads is a very bad sign to see. Avoid it at all costs.
First create the timer and configure it in the constructor:
private System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
private int hour = 0;
private int step = 0;
public Form1()
{
InitializeComponent();
timer.Tick += timer_Tick;
timer.Interval = 1000;
}
Have the Tick event do whatever should be done whenever it ticks.
private void timer_Tick(object sender, EventArgs e)
{
if (hour < 23)
{
hour += step;
lblTimerHour.Text = hour.ToString("00");
}
}
Then just start the timer when you want it to start ticking and stop the timer when you want it to stop:
private void btnHUp_MouseDown(object sender, MouseEventArgs e)
{
timer.Start();
}
private void btnHUp_MouseUp(object sender, MouseEventArgs e)
{
timer.Stop();
}
The timer will automatically ensure that the Tick event handler runs in the UI thread, and it won't block the UI thread (or any other thread) when its waiting for the next event to happen, it will just do nothing.
I have created a new Windows Forms Application with C# and .NET 4.0, and I have a function that must be called automatically every 60th of a second. My problem, is that I do not know where to call this function. .NET forms don't appear to have a built-in on-update event.
How would I go about getting this function called every 60th of a second?
Sorry if it is a beginner question.
You could set up a Timer to invoke a callback every 16 ms (which is 1/60 sec).
Important point as mentioned by #spender: If you are requiring very precise timing on this, eg exactly every 1/60 sec precision, you will not be satisfied with this solution. Windows does not natively do high-resolution timing in that vein very well. Props to #spender for the mention.
Here's a sample class with a rough outline of how it might look in a plain vanilla class, so you'd need to adapt it to your form:
You might also want to call this on a background thread, but if you're new to WinForms, we'll start in small chunks. Let's try the timer first, then go from there.
class Demo{
System.Timers.Timer myTimer;
void InitializeTimer(){
myTimer = new Timer(16); // elaps every 1/60 sec , appx 16 ms.
myTimer.ElapsedEventHandler+=new ElapsedEventHandler(myTimerEventHandler); //define a handler
myTimer.Enabled=true; //enable the timer.
}
void myTimerEventHandler(object sender, ElapsedEventArgs e){
// do your thing here
}
}
EDIT: Extra demo code for background thread creation and Invoked-based GUI update
As noted in my comment below, this is not as polished as I would like it, but I think it illustrates the salient points. It defines a BackgroundWorker thread to move the thread invocations to the background; the thread callback checks for the need to call Invoke, and calls right back to itself across a delegate invocation to allow for the custom form update in the "else" block of the "if (InvokeRequired)" statement. In a nutshell, a background thread starts, and starts a timer; when the timer elapses, it calls the updater on the background thread, which checks to see if Invoke must be called, and if it is, performs the thread context switch back to the GUI thread through the recall to the method, which then performs the GUI update. Put your custom update code in that "else" block. I hope this helps!!!
public partial class Form1 : Form
{
delegate void FormUpdateDelegate(object sender, ElapsedEventArgs e);
public BackgroundWorker backgroundThread;
System.Timers.Timer foo;
Random colorgen = new Random();
public Form1()
{
InitializeComponent();
backgroundThread = new BackgroundWorker();
backgroundThread.DoWork+=new DoWorkEventHandler(backgroundThread_DoWork);
backgroundThread.RunWorkerAsync();
}
public void formUpdater(object sender, ElapsedEventArgs e)
{
if (InvokeRequired)
{
FormUpdateDelegate d = new FormUpdateDelegate(formUpdater);
Invoke(d, new object[] { sender, e });
}
else
{
// Do your form update here
this.label1.ForeColor = Color.FromArgb(colorgen.Next());
}
}
public void backgroundThread_DoWork(object sender, DoWorkEventArgs e)
{
foo = new System.Timers.Timer(16);
foo.Elapsed += new ElapsedEventHandler(formUpdater);
foo.Start();
}
}
I just setup this in a console application, and it runs fine every second:
var timer = new System.Timers.Timer(1000);
timer.Enabled = true;
timer.Elapsed += new ElapsedEventHandler(delegate(object sender, ElapsedEventArgs eventArgs)
{
Console.WriteLine("Elapsed");
});
It is a bad practice to hope on system timers when you need precision at least 20 times per second.
I would recommend to use cycles like
Timespan timePerFrame = Timespan.FromMilliseconds(16);
while (_isRunning)
{
Stopwatch timer = Stopwatch.StartNew()
// Action.
while (timer.ElapsedMilliseconds < timePerFrame) { /* Nothing? */ }
}
That will give you the full precision, if your system supports high-precision stopwatch. (.IsHighResolution field).
I'm trying to use a Background Worker in a WPF application. The heavy lifting task uses WebClient to download some HTML and parse some info out of it. Ideally I want to do that downloading and parsing without locking the UI and placing the results in the UI once it's done working.
And it works fine, however, if I quickly submit the "download and parse" command, I get the error:
This BackgroundWorker is currently busy and cannot run multiple tasks
concurrently
So I did some Googling and it seems that I can enable the .WorkerSupportsCancellation property of the background worker and just .CancelAsync(). However, this doesn't work as expected (canceling the current download and parse).
I still get the above error.
Here's my code:
//In window constructor.
_backgroundWorker.WorkerSupportsCancellation = true;
_backgroundWorker.DoWork += new DoWorkEventHandler(_backgroundWorker_DoWork);
_backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_backgroundWorker_RunWorkerCompleted);
//Declared at class level variable.
BackgroundWorker _backgroundWorker = new BackgroundWorker();
//This is the method I call from my UI.
private void LoadHtmlAndParse(string foobar)
{
//Cancel whatever it is you're doing!
_backgroundWorker.CancelAsync();
//And start doing this immediately!
_backgroundWorker.RunWorkerAsync(foobar);
}
POCOClassFoo foo = new POCOClassFoo();
void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//This automagically sets the UI to the data.
Foo.DataContext = foo;
}
void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
//DOING THE HEAVY LIFTING HERE!
foo = parseanddownloadresult()!
}
Calling CancelAsync will still fire the RunWorkerCompleted event. In this event, you need to make sure that CancelAsync has not been called, by checking e.Cancelled. Until this event fires, you cannot call RunWorkerAsync.
Alternatively, I would recommend you do what Tigran suggested and create a new BackgroundWorker each time.
Further more, I would recommend storing the results of_backgroundWorker_DoWork in e.Result, then retrieve them from the same in _backgroundWorker_RunWorkerCompleted
Maybe something like this
BackgroundWorker _backgroundWorker;
private BackgroundWorker CreateBackgroundWorker()
{
var bw = new BackgroundWorker();
bw.WorkerSupportsCancellation = true;
bw.DoWork += _backgroundWorker_DoWork;
bw.RunWorkerCompleted += new _backgroundWorker_RunWorkerCompleted;
return bw.
}
private void LoadHtmlAndParse(string foobar)
{
//Cancel whatever it is you're doing!
if (_backgroundWorer != null)
{
_backgroundWorker.CancelAsync();
}
_backgroundWorker = CreateBackgroundWorker();
//And start doing this immediately!
_backgroundWorker.RunWorkerAsync(foobar);
}
//you no longer need this because the value is being stored in e.Result
//POCOClassFoo foo = new POCOClassFoo();
private void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
//Error handling goes here.
}
else
{
if (e.Cancelled)
{
//handle cancels here.
}
{
//This automagically sets the UI to the data.
Foo.DataContext = (POCOClassFoo)e.Result;
}
}
private void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
//DOING THE HEAVY LIFTING HERE!
e.Result = parseanddownloadresult()!
}
The thing is that CancelAsync() does what it climes: cancel in async way. That means that it will not stop immediately, but after some time. That time can never be calculated or predicted, so you have a couple of options:
Wait until this backround worker stops really, by waiting in cycle until IsBusy property of it becomes false
Or, I think, better solution is to start another background worker, considering that request of cancelation was already sent to the first one, so it will be soon or later stop. In this case, you need to know from which background worker data comes, in order to process it or not, cause on start of second the first one will still run and pump the data from WebService.
Hope this helps.
CancelAsync returns before the worker cancels and stops its work. Hence, your RunWorkerAsync call is starting before the worker is ready, and you're getting that error. You'll need to wait for the worker to be ready first.
When I'm not interested in tracking progress of an async operation, I tend to prefer to just slap a lambda at ThreadPool.QueueUserWorkItem instead of instantiating and setting up a background worker that I have to check the state of to be able to reuse in a sane way.
You need to verify before you kicks in.
f( !bw.IsBusy )
bw.RunWorkerAsync();
else
MessageBox.Show("Can't run the bw twice!");
You are calling CancelAsync without waiting for the background worker to actually cancel the work. Also you must have your own logic for cancelling the work. There is a good example on MSDN which shows how to do it. Basically in your parseanddownloadresult() method you need to check the CancellationPending property.
Hi i have stepped into some problem related to timer.
hope somebody can help..
I have a windows form containing a button
when i click on that button i start a parameterised thread
Thread thread1 = new Thread(new ParameterizedThreadStart( execute2));
thread1.Start(externalFileParams);
the code inside the thread executes very well
at the last line of this thread i start a timer
.
public void execute2(Object ob)
{
if (ob is ExternalFileParams)
{
if (boolean_variable== true)
executeMyMethod();//this also executes very well if condition is true
else
{
timer1.enabled = true;
timer1.start();
}
}
}
}
5 but the tick event of the timer is not fired
I am working on VS2008 3.5 framework. I have dragged the timer from toolbox and set its Interval to 300 also tried to set Enabled true/false
method is timer1_Tick(Object sender , EventArgs e) but its not fired
can anybody suggest what I am doing wrong?
You could try to start the timer this way:
Add in form constructor this:
System.Timers.Timer aTimer = new System.Timers.Timer();
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
// Set the Interval to 1 second.
aTimer.Interval = 1000;
Add this method to Form1:
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
//do something with the timer
}
On button click event add this:
aTimer.Enabled = true;
This timer is already threaded so no need to start a new thread.
It is true what MatÃas Fidemraizer says. But, there is a work around...
When you have a control on your form that is invokable (eg. a statusbar), just invoke that one!
C# Code sample:
private void Form1_Load(object sender, EventArgs e)
{
Thread sampleThread = new Thread(delegate()
{
// Invoke your control like this
this.statusStrip1.Invoke(new MethodInvoker(delegate()
{
timer1.Start();
}));
});
sampleThread.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
MessageBox.Show("I just ticked!");
}
System.Windows.Forms.Timer works in a single-threaded application.
Check this link:
http://msdn.microsoft.com/en-us/library/system.windows.forms.timer.aspx
Remarks says:
A Timer is used to raise an event at
user-defined intervals. This Windows
timer is designed for a
single-threaded environment where UI
threads are used to perform
processing. It requires that the user
code have a UI message pump available
and always operate from the same
thread, or marshal the call onto
another thread.
Read more "Remarks" section and you'll find that Microsoft recommends that you use this timer synchronizing it with the UI thread.
I would use a BackgroundWorker (instead of a raw thread). The main thread would subscribe to the worker's RunWorkerCompleted event: The event fires in your main thread when the thread completes. Use this event handler to restart your timer.