tags:

views:

1186

answers:

13

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?

+2  A: 

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.

Walt W
Not really, this seems very much like a polling loop to me which should be avoided whenever possible as it can really mess up power usage on laptops.
KeeperOfTheSoul
While you're right it might reduce battery a bit, I doubt it'd be that noticeable . . . We're talking about code that executes a tiny fraction of a millisecond. But, yeah, aquinas' answer is a more elegant approach.
Walt W
Remember that he's looking for every minute *on the minute* functionality here, so precision appears to be key. That's why it's hard to avoid a polling loop unless you do aquinas' method (and that yields accuracy sufficient for the application).
Walt W
+8  A: 

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.

Noldorin
I always assumed this just started up a background thread that marshaled events up to the UI thread periodically, but it looks like it calls into the native Windows UI.
Jason Jackson
@Jason: Yes, it's purely controlled by the message pump (I'm pretty confident). Fairly bad resolution, but no performance hit at least.
Noldorin
A: 

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.

Charles Bretana
Now this is definitely a polling loop which should be avoided whenever possible as it can really mess up power usage on laptops, especially when the timer could just be set to fire in 60 seconds rather than every second
KeeperOfTheSoul
A: 

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.

Brian Surowiec
+9  A: 

How about:

int startin = 60 - DateTime.Now.Second;
var t = new System.Threading.Timer(o => Console.WriteLine("Hello"), 
     null, startin * 1000, 60000);
aquinas
+1; i like it. should be some code added to keep it in sync.
Bdiem
+1: That's a nice way of dealing with the drift of a 60 second timer.
Walt W
It's not dealing with the drift ... the drift is about -1 millisecond in 8 seconds (on my machine). So there has to be a way to resync it.
Bdiem
I should have put a warning on this. If you REQUIRE the event to happen within the exact right second (on the minute in this case), then my solution should not be used and the only possible solution is to check every second. It's a *tiny* overheard. With my solution, suppose that it starts at 1:00.00.999, and then you tell it to fire again 1 minute later, there is a HIGH probability that the event won't fire EXACTLY 60000 milliseconds later. Ok, it probably will the first few times, but guaranteed you are losing ticks here and there. I did think this solution (in the abstract) was clever:)
aquinas
DateTime d = DateTime.Now; int startin = 60000 - (d.Second*1000) - d.Millisecond; var t = new System.Threading.Timer(o => Console.WriteLine("Hello"), null, startin, 60000);
richardtallent
A: 

Guys

Many thanks foer the answers. I like aquinas solution however I did state that I was using the .Net 2.0 framework so this rules out lambda expressions does it not ? Could I refactor this for 2.0 ?

Also, I am writing my code for the compact framework (Windows Mobile) and so want it to be as power efficient as possible. I do not want a timer polling incessently if possible. Also, as it happens, I now have a need to run code every 5 seconds (on the 5 seconds) so need a good solution to this more than ever.

Any further suggestions ? (Very grateful for the replies btw)

**1)** You should reply to answers either with comments on a specific answer, or edit (extend) your original question text. **2)** With C# language version 2, you can replace lambda functions with anonymous delegates: `delegate (parameter-list) { delegate-body }`
stakx
A: 

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)
Greg
A: 

I have tried to refactor aquinas code in a test environment thus.

        private void Test()
    {
        int startin = 5 - (DateTime.Now.Second % 5);
        System.Threading.TimerCallback timerDelegate = new System.Threading.TimerCallback (DoStuff);
        System.Threading.Timer timer = new System.Threading.Timer (timerDelegate,null,startin * 1000, 5000);  
     }

    private void DoStuff(object state)
    {                        
        string s = string.Format("Param is {0} ",DateTime.Now.ToLongTimeString ());
        System.Diagnostics.Debug.WriteLine (s );
    }

It seems very unreliable loosing a second every few seconds. Here's a typical debug output from the code above...

Param is 22:49:15 Param is 22:49:20 Param is 22:49:25 Param is 22:49:30 Param is 22:49:35 Param is 22:49:40 Param is 22:49:45 Param is 22:49:50 Param is 22:49:55 Param is 22:49:59 Param is 22:50:04 Param is 22:50:09 Param is 22:50:14 Param is 22:50:19 Param is 22:50:24 Param is 22:50:29 Param is 22:50:34 Param is 22:50:39 Param is 22:50:44 Param is 22:50:48 Param is 22:50:53 Param is 22:50:58 Param is 22:51:03 Param is 22:51:08 Param is 22:51:13 Param is 22:51:18 Param is 22:51:23 Param is 22:51:28 Param is 22:51:32 Param is 22:51:37 Param is 22:51:42 Param is 22:51:47 Param is 22:51:52 Param is 22:51:57 Param is 22:52:02 Param is 22:52:07 Param is 22:52:12 Param is 22:52:16 Param is 22:52:21 Param is 22:52:26 Param is 22:52:31

any suggestions ?

+1  A: 

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 //
}
Jayden
A: 

What about Quartz.NET? I think its a good framework to do timed actions.

Jehof
A: 

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
}
Ron Warholic
A: 

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
Jared
Why the down vote? I think this is a very good solution which will be pretty precise even after days or weeks!
gehho
A: 

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;
    }
}
John Leidegren