Windows Forms async await - c#

While I using winforms app, each 5 minute I need to check data updates. I need to send request to few service then get response and update data in database. What's is the best practices to make on another thread (or task ?)? The program should not slow down.
I try to make with timer:
Init timer when program is running
public class Timer
{
private System.Timers.Timer timer;
private void InitTimer()
{
timer = new System.Timers.Timer(4000);
timer.Elapsed += ElapsedTime;
timer.Enabled = true;
}
private void ElapsedTime()
{
//send request and update data
}
}

The way you are doing it will work just fine. The documentation for Sytem.Timers.Timer says:
If the SynchronizingObject property is null, the Elapsed event is raised on a ThreadPool thread.
The SynchronizingObject property is null by default, so your Elasped event will run on a ThreadPool thread, not on the UI thread. That means it will not stop your application from responding to user input.
If there is a chance that ElapsedTime() will run longer than your interval, and you don't want the events overlapping, then you can set AutoReset to false and reset it manually at the end of ElapsedTime(). Just make sure that everything is wrapped in a try/catch block, otherwise the timer won't get reset if there's an exception. My code below shows how that would look.
You don't need to use async/await anywhere here. Since it won't be running on the UI thread, using asynchronous code won't really help you any. In a desktop app, it's not a big deal to have a separate (non-UI) thread wait.
public class Timer
{
private System.Timers.Timer timer;
private void InitTimer()
{
timer = new System.Timers.Timer(4000);
timer.Elapsed += ElapsedTime;
timer.AutoReset = false;
timer.Enabled = true;
}
private void ElapsedTime()
{
try {
//send request and update data
}
catch (Exception e)
{
//log the error
}
finally
{
//start the timer again
timer.Enabled = true;
}
}
}

Related

Timer doesn't want to start again after it was disabled

I am writing a simple C# program that attempts to do something every x amount of seconds using System.Forms.Timer
The tick event calls a method that starts a new thread and disables the timer, then when the thread is done with its work, it enables the timer again, but the problem is, now it doesn't tick after it's been enabled.
static System.Windows.Forms.Timer testtimer = new System.Windows.Forms.Timer();
static void Main()
{
testtimer.Tick += testtimertick;
testtimer.Interval = 5000;
testtimer.Enabled = true;
testtimer.Start();
while (true)
{
Application.DoEvents(); //Prevents application from exiting
}
}
private static void testtimertick(object sender, System.EventArgs e)
{
testtimer.Enabled = false;
Thread t = new Thread(dostuff);
t.Start();
}
private static void dostuff()
{
//Executes some code
testtimer.Enabled = true; //Re enables the timer but it doesn't work
testtimer.Start();
}
As #grzenio said, it appears that your issue has to do with the fact that you are making cross thread calls to a Windows Form Control that was created on a different thread.
If you are using .NET 4.5 (C# 5.0), I would suggest looking at the async/await keywords, a good introduction can be found at Stephen Cleary's Blog
An example of how you could use async and await with your legacy "DoStuff":
private async void _Timer_Tick(object sender, EventArgs e)
{
_Timer.Enabled = false;
await Task.Run((() => DoStuff()));
_Timer.Enabled = true;
}
Things to notice:
async was added to the Timer_Tick event's signature.
The await keyword along with Task.Run was used to asynchronously run the DoStuff.
When using these keywords, the DoStuff will be run asynchronously and once DoStuff returns, it will continue on the line after await using the context of the thread that originally called Tick.
Don't use a GUI timer without a GUI. Don't spin with DoEvents because you are burning 100% of a CPU core with that. Use a System.Threading.Timer. It will just work.
Windows Forms controls are not thread safe, you should make sure you use them on the UI thread, see e.g. C# Windows Forms Application - Updating GUI from another thread AND class?
You can use System.Threading.Timer to do what you want to do, using the Change Method to set the time and the Period, Just restart it when you finish your work.
class Program
{
static System.Threading.Timer testtimer;
static void Main(string[] args)
{
testtimer = new System.Threading.Timer(testtimertick);
testtimer.Change(5000,0);
Console.ReadLine();
}
private static void testtimertick(object sender)
{
Thread t = new Thread(dostuff);
t.Start();
}
private static void dostuff()
{
//Executes some code
Thread.Sleep(2000);
Console.WriteLine("Tick");
testtimer.Change(5000, 0);
}
}
static System.Windows.Forms.Timer testtimer = new System.Windows.Forms.Timer();
static void Main()
{
testtimer.Tick += testtimertick;
testtimer.Interval = 5000;
testtimer.Enabled = true;
while (true)
{
Application.DoEvents(); //Prevents application from exiting
}
}
private static void testtimertick(object sender, System.EventArgs e)
{
Thread t = new Thread(dostuff);
t.Start();
}
private static void dostuff()
{
testtimer.Enabled = false;
//Executes some code
testtimer.Enabled = true; //Re enables the timer but it doesn't work
testtimer.Start();
}
I had a similar issue just now. I was disabling the timer and enabling again whenever I want.
The next time when I enable, it won't work.
I tried disposing the Timer object when I want to disable and creating new instance of it each time I want to enable it. Didn't work though.
Figured out a solution then. I'm removing the event which is configured in testtimer.Tick, and then adding it back when I want to enable the timer.
So the timer internally will be always instantiated with valid values and have its property Enabled = true. The only difference is that it won't have anything actually to perform whenever a tick event triggers.
This would imitate disabling and enabling the timer and makes it working as good as you control like Enabled = false / true.
If you really want to stick to the GUI timer, and start it from non UI thread, you can try to do similar stuff, then write to GUI from non UI thread.
Not the ideal solution, I know.
this.Invoke((MethodInvoker)delegate
{
refreshTimer.Enabled = true;
refreshTimer.Start();
});

Alternative to Thread.Sleep

Working on a windows service, which has to process request in every predefined interval of time. Thread.Sleep does the work perfectly fine but problem with this is when service is invoked to be stopped, service freeze if thread is in sleep mode.
I have read about the alternative approach like Timer, but problem with that is after that defined interval new thread is getting started.
Is there a better way to achieve same result and not run in to issue.
What you're looking for is the ability to respond to the notification of two different events - (1) when the timer elapses and (2) when the service is stopped. #Anurag Ranhjan is on the right track with WaitHandle, but you have two events, not one. To properly handle this, do the following.
First, define the two events you care about using ManualResetEvent. You can use AutoResetEvent if you prefer; I just prefer resetting the events manually.
using System.Threading;
ManualResetEvent shutdownEvent = new ManualResetEvent();
ManualResetEvent elapsedEvent = new ManualResetEvent();
You need to trigger these events when they occur. For the shutdownEvent, it's easy. In the OnStop callback of your Windows service, just set the event.
protected override void OnStop
{
shutdownEvent.Set();
}
For the elapsedEvent, you could do this a couple different ways. You could create a background thread, i.e., the ThreadPool, that uses Thread.Sleep. When the thread wakes up, set the elapsedEvent and go back to sleep. Since it's a background thread, it won't hang your service when it shuts down. The alternative, as you've already suggested, is to use a timer. This is how I do it.
using System.Timers;
Timer timer = new Timer();
timer.Interval = 5000; // in milliseconds
timer.Elapsed += delegate { elapsedEvent.Set(); };
timer.AutoReset = false; // again, I prefer manual control
timer.Start();
Now that you've got events being set properly, put them in a WaitHandle array.
WaitHandle[] handles = new WaitHandle[]
{
shutdownEvent,
elapsedEvent
};
Instead of the WaitHandle.WaitOne method, use the WaitHandle.WaitAny method inside a while loop, like this.
while (!shutdownEvent.WaitOne())
{
switch (WaitHandle.WaitAny(handles))
{
case 0: // The shutdownEvent was triggered!
break;
case 1: // The elapsedEvent was triggered!
Process(); // do your processing here
elapsedEvent.Reset(); // reset the event manually
timer.Start(); // restart the timer manually
break;
default:
throw new Exception("unexpected switch case");
}
}
I've condensed this example from production code in my project. I know this mechanism works, but I may have missed something in the writeup. Let me know if you have any questions.
You can use WaitHandle.WaitOne instead. You can wait for closing event to trigger or timeout that you are specifying in predefined interval of time.
static AutoResetEvent seviceStopRequested = new AutoResetEvent(false);
....
((AutoResetEvent)stateInfo).WaitOne([timeout], false)
Then when Service stop is invoked, you can just trigger the event
seviceStopRequested .Set();
I usually use the following pattern:
public class MyJob
{
System.Threading.Timer _timer;
bool _isStopped;
public void MyJob()
{
_timer = new Timer(OnWork, null, TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(-1));
}
private void OnWork(object state)
{
//[.. do the actual work here ..]
if (!_isStopped)
_timer.Change(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(-1));
}
public void Stop()
{
_isStopped = true;
_timer.Change(TimeSpan.FromSeconds(-1), TimeSpan.FromSeconds(-1));
}
public void Start()
{
_isStopped = false;
_timer.Change(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(-1));
}
}
Key points:
Only using the initial interval gives you full control of when the timer is started again (i.e. the work time is not counted in the timer interval)
Changing the timer to -1 seconds pauses it until changed again
It should therefore work with all your requirements.
Use a Timer to add commands/tasks including the task for shutdown to a blocking queue. Make your service thread to wait for tasks on the blocking queue and execute them when available. The timer thread will keep adding the tasks to the queue periodically.
For what it is worth most of the blocking calls in the .NET BCL will respond to Thread.Interrupt. That is, they will not wait for the full amount of time specified when called and instead return immediately. However, I would avoid using this method and instead use a single ManualResetEvent to perform both the idle waiting and the shutdown signal. It would look like this.
public class MyServer : ServiceBase
{
private ManualResetEvent shutdown = new ManualResetEvent(false);
protected override void OnStart(string[] args)
{
new Thread(
() =>
{
while (!shutdown.WaitOne(YourInterval))
{
// Do work here.
}
}).Start();
}
protected override void OnStop()
{
shutdown.Set();
}
}

Windows Forms Timer doesn't stop. How is that possible?

During debugging I can see that after Timer.Stop() or Timer.Enabled = false commands are executed, Timer is still running (Timer.Enabled = true). How is that possible?
This is possible when you stop the timer on a worker thread. For example:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
Timer timer1;
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
timer1 = new Timer();
timer1.Interval = 3000;
timer1.Start();
var t = new System.Threading.Thread(stopTimer);
t.Start();
}
private void stopTimer() {
timer1.Enabled = false;
System.Diagnostics.Debug.WriteLine(timer1.Enabled.ToString());
}
}
Output:
True
The timer must be stopped by the UI thread, the class takes care of it automatically. Quite similar to Control.BeginInvoke(). There's an explicit race, the Tick event handler can run after you stopped it. This can also happen on the UI thread if the very first timer you create is created on a worker thread. A splash screen for example. That's not healthy, you ought to fix that.
Calling Start after you have disabled a Timer by calling Stop will cause the Timer to restart the interrupted interval. If your Timer is set for a 5000-millisecond interval, and you call Stop at around 3000 milliseconds, calling Start will cause the Timer to wait 5000 milliseconds before raising the Tick event.
bear also in mind
Calling Stop on any Timer within a Windows Forms application can cause messages from other Timer components in the application to be processed immediately, because all Timer components operate on the main application thread. If you have two Timer components, one set to 700 milliseconds and one set to 500 milliseconds, and you call Stop on the first Timer, your application may receive an event callback for the second component first. If this proves problematic, consider using the Timer class in the System.Threading namespace instead.
http://msdn.microsoft.com/en-us/library/system.windows.forms.timer.stop.aspx
public void EnableTimer(bool state)
{
if (this.InvokeRequired) {
this.Invoke(new Action<bool>(EnableTimer), state);
} else {
this.Timer1.Enabled = state;
}
}
Try this code...

BackgroundWorker thread and Timer logic

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().

Quick C# threading class

Got a quick question on creating a C# thread.
It needs to run every 10 minutes
The worker will do work then go into sleep for another 10 minutes
It can also be triggered to run immediately by calling Trigger()
It can be stopped by calling Stop()
I've created one with ManualResetEvent, which is set when Stop() is called. This works well but do I need to create another ManualResetEvent or wait handle in order to be able to trigger the worker immediately?
If by Stop you mean Stop waiting and don't run again then I think a Threading.Timer will be a good (lean) choice.
You can activate a timer with a DoWork() method and a 10 minutes interval. It will use the ThreadPool which seems the best choice here. Trigger() can queue DoWork directly on the Pool and Stop() can deactivate the Timer. DoWork() shouldn't use Sleep() and be suitable for the ThreadPool.
Using a timer here makes sense. Here's a quick code-snippet which I haven't tested.
private System.Timers.Timer _timer = null;
public void Constructor()
{
_timer = new System.Timers.Timer(600000);
_timer.Elapsed += new ElapsedEventHandler(t_Elapsed);
}
public void ForceDoWork()
{
//unsubscribe to timer event, so work dowsnt get fired twice
_timer.Elapsed -= new ElapsedEventHandler(t_Elapsed);
StartWorking();
_timer.Elapsed += new ElapsedEventHandler(t_Elapsed);
}
public void StartWorking()
{
ThreadPool.QueueUserWorkItem(new WaitCallback(WorkToBeDone));
}
private void WorkToBeDone(object state)
{
//work here
}
public void t_Elapsed(object sender, ElapsedEventArgs e)
{
StartWorking();
}
Yes, you'll need another wait handle to force thread to execute. You'll basically have to WaitOne for this handle for 10 minutes, thus either elapsing a timeout or continuing whenever the wait handle becomes signalled.
Don't forget to switch that handle to a non-signalled state, though.

Categories