Reputation:
I want to do stuff every minute on the minute (by the clock) in a windows forms app using c#. I'm just wondering whats the best way to go about it ?
I could use a timer and set its interval to 60000, but to get it to run on the minute, I would have to enable it on the minute precisely, not really viable.
I could use a timer and set its interval to 1000. Then within its tick event, I could check the clocks current minute against a variable that I set, if the minute has changed then run my code. This worries me because I am making my computer do a check every 1 second in order to carry out work every 1 minutes. Surely this is ugly ?
I'm using windows forms and .Net 2.0 so do not want to use the DispatchTimer
that comes with .Net 3.5
This must be a fairly common problem. Have any of you a better way to do this?
Upvotes: 47
Views: 89930
Reputation: 11
I have a solution based on Environment.TickCount
static void Main(string[] args)
{
//constatnt total miliseconds to one minute
const Int32 minuteMilisecond = 60 * 1000;
//get actual datetime
DateTime actualDateTime = DateTime.UtcNow;
//compenzation to one minute
Int32 nexTimer = Environment.TickCount + ((59 - actualDateTime.Second) * 1000) + (999 - actualDateTime.Millisecond);
//random fuction to simulate different delays on thread
Random rnd = new Random();
//main loop
while (true)
{
if (Environment.TickCount > nexTimer)
{
nexTimer += minuteMilisecond;
//execute your code here every minute
Console.WriteLine($"actual DateTime: {DateTime.Now.ToString("yyyy.MM.dd HH:mm:ss:ffff")}");
}
//random sleep between 100 - 200 ms
Thread.Sleep(rnd.Next(100, 200));
}
}
Upvotes: -1
Reputation: 463
By making use of ReactiveExtensions you could use the following code if you were interested in doing something as simple as printing to the console.
using System;
using System.Reactive.Linq;
namespace ConsoleApplicationExample
{
class Program
{
static void Main()
{
Observable.Interval(TimeSpan.FromMinutes(1))
.Subscribe(_ =>
{
Console.WriteLine(DateTime.Now.ToString());
});
Console.WriteLine(DateTime.Now.ToString());
Console.ReadLine();
}
}
}
Upvotes: 3
Reputation: 169
Create a method or put this code where you want the timer to start:
int time = 60 - DateTime.Now.Second; // Gets seconds to next minute
refreshTimer.Interval = time * 1000;
refreshTimer.Start();
And then on your tick event set the interval to 60000:
private void refreshTimer_Tick(object sender, EventArgs e)
{
refreshTimer.Interval = 60000; // Sets interval to 60 seconds
// Insert Refresh logic
}
Upvotes: 5
Reputation: 29786
You can nail this with reactive extensions which will take care of lots of timer related problems for you (clock changes, app hibernation etc). Use Nuget package Rx-Main and code like this:
Action work = () => Console.WriteLine(DateTime.Now.ToLongTimeString());
Scheduler.Default.Schedule(
// start in so many seconds
TimeSpan.FromSeconds(60 - DateTime.Now.Second),
// then run every minute
() => Scheduler.Default.SchedulePeriodic(TimeSpan.FromMinutes(1), work));
Console.WriteLine("Press return.");
Console.ReadLine();
Read here (search for "Introducing ISchedulerPeriodic") to see all the issues this is taking care of: http://blogs.msdn.com/b/rxteam/archive/2012/06/20/reactive-extensions-v2-0-release-candidate-available-now.aspx
Upvotes: 9
Reputation: 61017
I jsut wrote this class using the WPF DispatcherTimer but you can swap the dispatcher for any timer that supports changing when it's woken from sleep state.
The class is constructed with a fixed time step and supprts Start/Stop/Reset, Start/Stop/Start works like a resume operation. The timer is like a stopwatch in that regard.
A clock implementation would simply create the class with a interval of 1 second and listen to the event. Be wary though that this is a real-time clock, if the tick event takes longer than the interval to finish you'll notice that the clock will try and catch up to real-time this will cause a burst of tick events being raised.
public class FixedStepDispatcherTimer
{
/// <summary>
/// Occurs when the timer interval has elapsed.
/// </summary>
public event EventHandler Tick;
DispatcherTimer timer;
public bool IsRunning { get { return timer.IsEnabled; } }
long step, nextTick, n;
public TimeSpan Elapsed { get { return new TimeSpan(n * step); } }
public FixedStepDispatcherTimer(TimeSpan interval)
{
if (interval < TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException("interval");
}
this.timer = new DispatcherTimer();
this.timer.Tick += new EventHandler(OnTimerTick);
this.step = interval.Ticks;
}
TimeSpan GetTimerInterval()
{
var interval = nextTick - DateTime.Now.Ticks;
if (interval > 0)
{
return new TimeSpan(interval);
}
return TimeSpan.Zero; // yield
}
void OnTimerTick(object sender, EventArgs e)
{
if (DateTime.Now.Ticks >= nextTick)
{
n++;
if (Tick != null)
{
Tick(this, EventArgs.Empty);
}
nextTick += step;
}
var interval = GetTimerInterval();
Trace.WriteLine(interval);
timer.Interval = interval;
}
public void Reset()
{
n = 0;
nextTick = 0;
}
public void Start()
{
var now = DateTime.Now.Ticks;
nextTick = now + (step - (nextTick % step));
timer.Interval = GetTimerInterval();
timer.Start();
}
public void Stop()
{
timer.Stop();
nextTick = DateTime.Now.Ticks % step;
}
}
Upvotes: 5
Reputation: 1325
Building on the answer from aquinas which can drift and which doesn't tick exactly on the minute just within one second of the minute:
static System.Timers.Timer t;
static void Main(string[] args)
{
t = new System.Timers.Timer();
t.AutoReset = false;
t.Elapsed += new System.Timers.ElapsedEventHandler(t_Elapsed);
t.Interval = GetInterval();
t.Start();
Console.ReadLine();
}
static double GetInterval()
{
DateTime now = DateTime.Now;
return ((60 - now.Second) * 1000 - now.Millisecond);
}
static void t_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
Console.WriteLine(DateTime.Now.ToString("o"));
t.Interval = GetInterval();
t.Start();
}
On my box this code ticks consistently within .02s of each minute:
2010-01-15T16:42:00.0040001-05:00
2010-01-15T16:43:00.0014318-05:00
2010-01-15T16:44:00.0128643-05:00
2010-01-15T16:45:00.0132961-05:00
Upvotes: 60
Reputation: 10074
Alternatively, you could sleep to pause execution until it times out which should be close to your desired time. This will only wake the computer when the sleep finishes so it'll save you CPU time and let the CPU power down between processing events.
This has the advantage of modifying the timeout so that it will not drift.
int timeout = 0;
while (true) {
timeout = (60 - DateTime.Now.Seconds) * 1000 - DateTime.Now.Millisecond;
Thread.Sleep(timeout);
// do your stuff here
}
Upvotes: 1
Reputation: 2778
You could set up two timers. An initial short interval timer (perhaps to fire every second, but dependent on how presice the second timer must fire on the minute).
You would fire the short interval timer only until the desired start time of the main interval timer is reached. Once the initial time is reached, the second main interval timer can be activated, and the short interval timer can be deactivated.
void StartTimer()
{
shortIntervalTimer.Interval = 1000;
mainIntervalTimer.Interval = 60000;
shortIntervalTimer.Tick +=
new System.EventHandler(this.shortIntervalTimer_Tick);
mainIntervalTimer.Tick +=
new System.EventHandler(mainIntervalTimer_Tick);
shortIntervalTimer.Start();
}
private void shortIntervalTimer_Tick(object sender, System.EventArgs e)
{
if (DateTime.Now.Second == 0)
{
mainIntervalTimer.Start();
shortIntervalTimer.Stop();
}
}
private void mainIntervalTimer_Tick(object sender, System.EventArgs e)
{
// do what you need here //
}
Upvotes: 1
Reputation: 35544
What about Quartz.NET? I think its a good framework to do timed actions.
Upvotes: 2
Reputation: 2179
What about a combination of aquinas' answer and 'polling': (apologies for the mixture of languages)
def waitForNearlyAMinute:
secsNow = DateTime.Now.Second;
waitFor = 55 - secsNow;
setupTimer(waitFor, pollForMinuteEdge)
def pollForMinuteEdge:
if (DateTime.Now.Second == 0):
print "Hello, World!";
waitForNearlyAMinute();
else:
setupTimer(0.5, pollForMinuteEdge)
Upvotes: 0
Reputation: 23796
How about:
int startin = 60 - DateTime.Now.Second;
var t = new System.Threading.Timer(o => Console.WriteLine("Hello"),
null, startin * 1000, 60000);
Upvotes: 30
Reputation: 147370
Creating a Timer
control that fires every 1 second (and usually does nothing but a simple check) will add negligible overhead to your application.
Simply compare the value of Environment.TickCount
or DateTime.Now
to the last stored time (the previous 'minute tick'), and you should have a reasonably precise solution. The resolution of these two time values is about 15ms, which should be sufficient for your purposes.
Do note however that the interval of the Timer
control is not guaranteed to be that precise or even anywhere now, since it runs on the Windows message loop, which is tied in with the responsiveness of the UI. Never rely on it for even moderately precise timing - though it is good enough for firing repeating events where you can check the time using a more sensitive method such as one of the two given above.
Upvotes: 9
Reputation: 17291
What I'm using for scheduled tasks is a System.Threading.Timer(System.Threading.TimerCallback, object, int, int) with the callback set to the code I want to execute based on the interval which is supplied in milliseconds for the period value.
Upvotes: 0
Reputation: 146499
Use a timer set to run every second (or millisecond, whatever your accuracy threshold is), and then code the method to run your functionality if and only if the current time is within that threshold past the "on the minute" point.
Upvotes: 0
Reputation: 3349
Running a bit of code to see if the minute has changed once per second should not require much CPU time, and should be acceptable.
Upvotes: 1