Related
I have an application that generates random numbers for about 20 seconds and shows the random number on the fly in a label in the screen.
I want to show the numbers in the same label but then slow down the display of the numbers so like 5 seconds before stoping the process, the display of the number should smoothly slow down more and more until it stops in the final number. Like a raffle.
Any clue?
I can start by telling you what not do to. Do not use Thread.Sleep -- doing so is almost always a "worst practice" and will make your UI unresponsive.
If you use Thread.Sleep on a second thread, as mcl suggests, you won't freeze your UI but you are burning an extremely expensive thread to do very little work.
If you are using C# 4 or earlier then I would create a timer set to tick, say, four times a second. Handle the tick event, and if enough time has passed since the last tick event, change the label. Or, change the interval of the timer each time it ticks.
If you are using C# 5, you can just use await Task.Delay(x):
async void Animate()
{
int delay = 5;
for(int i = 1; i < 10; ++i)
{
UpdateLabel();
await Task.Delay(delay);
delay = delay * 2;
}
}
So now you start with a 5ms delay, then 10, then 20...
Here's a working program to get you started. It changes the Text of the Form for 3 seconds quickly, after which it gets slower. That's achieved by using one Timer to start decelerating the Timer which shows the random numbers.
public partial class Form1 : Form
{
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer() { Interval = 10 };
System.Windows.Forms.Timer timerForStartingSlowDown = new System.Windows.Forms.Timer() { Interval = 3000 };
bool slow = false;
Random random = new Random();
public Form1()
{
InitializeComponent();
timer.Tick += timer_Tick;
timerForStartingSlowDown.Tick += timerForStartingSlowDown_Tick;
Shown += Form1_Shown;
}
void timerForStartingSlowDown_Tick(object sender, EventArgs e)
{
slow = true;
timerForStartingSlowDown.Enabled = false;
}
void Form1_Shown(object sender, EventArgs e)
{
timer.Enabled = true;
timerForStartingSlowDown.Enabled = true;
}
void timer_Tick(object sender, EventArgs e)
{
if (timer.Interval > 350) timer.Enabled = false;
else
{
if (slow) timer.Interval += 10;
Text = random.Next(1, 100).ToString();
}
}
}
Consider generating those numbers on a different thread. You can use BackgroundWorker for that and report the progress as you generate each number. When you begin to reach the end use Thread.Sleep(miliseconds) to "slow" (freez) the BackgroundWorker's job thread that is generating the numbers for a specified amount of miliseconds increasing those miliseconds as you aproach the final number. That should do the trick.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
Set the BackgroundWorker to report progress and use that to "push" the generated number to the UI thread.
You can also drag and drop the BackgroundWorker component on your form from the Toolbox.
I am trying to get a timer run every minute in sync with the system clock (00:01:00, 00:02:00, 00:03:00, etc). This is my code.
private System.Timers.Timer timer;
public frmMain()
{
timer = new System.Timers.Timer();
timer.AutoReset = false;
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
timer.Interval = GetInterval();
timer.Start();
}
private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
System.Diagnostics.Trace.WriteLine(DateTime.Now.ToString("hh:mm:ss tt"));
timer.Interval = GetInterval();
timer.Start();
}
private double GetInterval()
{
DateTime now = DateTime.Now;
return ((60 - now.Second) * 1000 - now.Millisecond);
}
It runs perfectly on my home PC.
12:12:00 AM
12:13:00 AM
12:14:00 AM
12:15:00 AM
12:16:00 AM
12:17:00 AM
12:18:00 AM
12:19:00 AM
12:20:00 AM
12:21:00 AM
However I'm getting weird results on my VPS (windows server 2003).
12:11:59 AM
12:12:59 AM
12:13:00 AM
12:13:59 AM
12:14:00 AM
12:14:59 AM
12:15:00 AM
12:15:59 AM
12:16:00 AM
12:16:59 AM
12:17:00 AM
12:17:59 AM
12:18:00 AM
12:18:59 AM
12:19:00 AM
12:19:59 AM
12:20:00 AM
12:20:59 AM
12:21:00 AM
Is it because System.Timers.Timer does not work well on windows server 2003? Or is it an issue with my VPS?
Instead of using DateTime.Now and pulling the individual parts, just use the Ticks. Get the ticks when you start, then calculate what the ticks should be for the next timer tick. Once that timer tick occurs use the last value to calculate what the next value should be.
Example:
private const long MILLISECOND_IN_MINUTE = 60 * 1000;
private const long TICKS_IN_MILLISECOND = 10000;
private const long TICKS_IN_MINUTE = MILLISECOND_IN_MINUTE * TICKS_IN_MILLISECOND;
private System.Timers.Timer timer;
private long nextIntervalTick;
public void frmMain()
{
timer = new System.Timers.Timer();
timer.AutoReset = false;
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
timer.Interval = GetInitialInterval();
timer.Start();
}
private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
System.Diagnostics.Trace.WriteLine(DateTime.Now.ToString("hh:mm:ss tt"));
timer.Interval = GetInterval();
timer.Start();
}
private double GetInitialInterval()
{
DateTime now = DateTime.Now;
double timeToNextMin = ((60 - now.Second) * 1000 - now.Millisecond) + 15;
nextIntervalTick = now.Ticks + ((long)timeToNextMin * TICKS_IN_MILLISECOND);
return timeToNextMin;
}
private double GetInterval()
{
nextIntervalTick += TICKS_IN_MINUTE;
return TicksToMs(nextIntervalTick - DateTime.Now.Ticks);
}
private double TicksToMs(long ticks)
{
return (double)(ticks / TICKS_IN_MILLISECOND);
}
You could probably do this using Seconds and Milliseconds like you were. The trick is to have one starting point to calculate off of (rather then determining how many seconds to the next minute). If there are additional concerns not mentioned in the original problem, like the code in timer_Elapsed might take longer then a minute to run, then you will need to add code to handle this.
Please leave a comment if you need additional help. Otherwise please select a correct answer.
((60 - now.Second) * 1000 - now.Millisecond)
This means that if now.Second happens to be 59 your time will fire again in less than a second. This is the reason for your weird results (the timer not firing at exactly 0 second offsets).
It's probably more productive for you to have the timer fire every second, keep the previous date/time value in a separate variable, and update the on-screen timer when the second portion changes.
Normal timers like System.Timers.Timer are not accurate and not nearly good enough to achieve a 1 msec interval.
Firstly they have an internal update rate of 10-15 msec. Secondly depending on the system other threads may run for ~15 msec delaying your timer before Windows forces them to yield.
If you want more accuracy than Timer use System.Diagnostics.Stopwatch as reported in another thread it can go from 0.3 ms and is integrated with your .NET environment.
Another option is to use a multimedia time (accurate to around 1ms).
Either way here is an excellent tutorial on the issue.
Breaking it down:
Timer drift normally adds a delay to the timer. But you are seeing the opposite happen. As timers do not have millisecond accuracy (they are only accurate to in the 15ms range) they will often be fired with that granularity. So in effect firing the timer a few milliseconds before the minute mark on some occasions (causing it to fire immediately afterwards aswell). If you require it to only fire in the new minute I would add in a few milliseconds of a wait time to compensate (5ms should do it).
Your home pc is not so fast (which means it exhibits extra timer drift dealing with the timer handler) and normally fires the event in the next second. Your work PC sometimes manages to handle the timer event quick enough that it records 59 seconds past (which I do believe is truncated and probably 59.900 ~ 59.999). This may also occur if the machine is multi-cored as there is no thread yeilding delay and the timer can be fired very quickly.
That is the cause of your Timer irregularities.
Hi another example is use the Timer from System.Windows.Threading.
using System;
using System.Windows.Threading;
namespace Yournamespace
{
public partial class TestTimer
{
DispatcherTimer dispatcherTimer1m;
public TestTimer()
{
dispatcherTimer1m = new DispatcherTimer();
dispatcherTimer1m.Tick += new EventHandler(DispatcherTimer1m_Tick);
dispatcherTimer1m.Interval = TaskHelper.GetSyncIntervalms;
dispatcherTimerm.Start();
}
private void DispatcherTimer1m_Tick(object sender, EventArgs e)
{
try
{
dispatcherTimer1m.Stop();
//Do your effort here
}
catch (Exception exc)
{
//Your exception handled here
}
finally
{
dispatcherTimer1m.Interval = TaskHelper.GetSyncInterval1m;
dispatcherTimer1m.Start();
}
}
}
public class TaskHelper
{
private const ushort internalUpdate = 15;//ms
public static TimeSpan GetSyncInterval1m => new TimeSpan(0, 0, 0, 60,internalUpdate).Subtract( new TimeSpan(0, 0, 0, DateTime.Now.Second, 0));
}
}
Remember that Windows Server is by default set up to share resources with background tasks more willingly than the client versions so timer accuracy can be affected if the server is running a number of background tasks.
You could try temporarily changing it to prioritise the foreground task to see if that gives different results - the setting is somewhere in the System control panel, you're looking for two radio buttons, one that says "Programs" and one that says "Background services" or similar.
What is the best way to simply wait for 24 hours +/- 1 second. I know that Threading.Sleep for a long time is not accurate and can vary based on the CPU loads.
I see the System.Timers.Timer allows you to create a timed event. How do I use this to simply wait for 24 hours?
private void myTest{
// SET SOMETHING UP
m_theTimer = new System.Timers.Timer();
m_theTimer.Elapsed += new ElapsedEventHandler(OurTimerCallback);
const int hrsToMs = 60 * 60 * 1000;
m_theTimer.Interval = TestPeriodHours * hrsToMs;
m_theTimer.Enabled = true;
//---->want to wait for 24 hours<------
// RESUME TEST HERE
VerifySomething()
}
public void OurTimerCallback(object source, ElapsedEventArgs e)
{
Console.WriteLine("Received a callback, the time is {0}", e.SignalTime);
}
Maybe Quartz.NET is what you are looking for. http://quartznet.sourceforge.net/.
+/- 1 second? I would use a hybrid approach.
Use task scheduler to start a program, in 23:58. Then have that program sleep for 2 minutes or less until the exact moment arrives, then perform the work.
If you don't care about the 1 second accuracy, you could just use task scheduler alone, on the 24 hour division.
Assuming you are on win forms.
Declare a datetime variable and assign a value on form_load event.
Create a timer and fire the the event in every second.
Check the datetime variable in the timer event. if timespan is 1 hour, call your main method.
I think you can use a combination of ManualResetEvent along with Timer.
Maybe this is better if you really want to use a timer.
private void myTest{
// SET SOMETHING UP
bool bTimer_Expired = true;
m_theTimer = new System.Timers.Timer();
m_theTimer.Elapsed += new ElapsedEventHandler(OurTimerCallback);
const int hrsToMs = 60 * 60 * 1000;
m_theTimer.Interval = TestPeriodHours * hrsToMs;
m_theTimer.Enabled = true;
//Wait for call back to set flag that the elapsed time has expired
while(!bTimer_Expired)Sleep(1000);
//---->want to wait for 24 hours<------
// RESUME TEST HERE
VerifySomething()
public void OurTimerCallback(object source, ElapsedEventArgs e)
{
bTimer_Expired=true;
Console.WriteLine("Received a callback, the time is {0}", e.SignalTime);
}
But I'm a little fuzzy on why ManuelResetEvent doesn't do the job easier....
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?
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
How about:
int startin = 60 - DateTime.Now.Second;
var t = new System.Threading.Timer(o => Console.WriteLine("Hello"),
null, startin * 1000, 60000);
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.
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
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;
}
}
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
}
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();
}
}
}
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.
What about Quartz.NET? I think its a good framework to do timed actions.
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 //
}
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
}
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.
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.
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)
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));
}
}
I'm working on a little web crawler that will run in the system tray and crawl a web site every hour on the hour.
What is the best way to get .NET to raise an event every hour or some other interval to perform some task. For example I want to run an event every 20 minutes based on the time. The event would be raised at:
00:20
00:40
01:00
01:20
01:40
and so on. The best way I can think of to do this is by creating a loop on a thread, that constantly checks if the time is divisible by a given interval and raises a callback event if the time is reached. I feel like there has got to be a better way.
I'd use a Timer but I'd prefer something that follows a "schedule" that runs on the hour or something along those lines.
Without setting up my application in the windows task scheduler is this possible?
UPDATE:
I'm adding my algorithm for calculating the time interval for a timer. This method takes a "minute" parameter, which is what time the timer should trigger a tick. For example, if the "minute" parameter is 20, then the timer will tick at the intervals in the timetable above.
int CalculateTimerInterval(int minute)
{
if (minute <= 0)
minute = 60;
DateTime now = DateTime.Now;
DateTime future = now.AddMinutes((minute - (now.Minute % minute))).AddSeconds(now.Second * -1).AddMilliseconds(now.Millisecond * -1);
TimeSpan interval = future - now;
return (int)interval.TotalMilliseconds;
}
This code is used as follows:
static System.Windows.Forms.Timer t;
const int CHECK_INTERVAL = 20;
static void Main()
{
t = new System.Windows.Forms.Timer();
t.Interval = CalculateTimerInterval(CHECK_INTERVAL);
t.Tick += new EventHandler(t_Tick);
t.Start();
}
static void t_Tick(object sender, EventArgs e)
{
t.Interval = CalculateTimerInterval(CHECK_INTERVAL);
}
System.Timers.Timer. If you want to run at specific times of the day, you will need to figure out how long it is until the next time and set that as your interval.
This is just the basic idea. Depending on how precise you need to be you can do more.
int minutes = DateTime.Now.Minute;
int adjust = 10 - (minutes % 10);
timer.Interval = adjust * 60 * 1000;
You may find help from Quartz.net http://quartznet.sourceforge.net/
Here is an example of a lightweight system using thread timing and an asynch call.
I know there are some downsides, but I like using this instead of a timer when kicking off a long running process (like schedualed backend services). Since it runs inline in the timer thread, you don't have to worry about it getting kicked off again before the the original call has finished. This could be extended quite a bit to make it use an array of datetimes as the trigger times or add some more abilities to it. I am sure some of you guys out there know some better ways.
public Form1()
{
InitializeComponent();
//some fake data, obviously you would have your own.
DateTime someStart = DateTime.Now.AddMinutes(1);
TimeSpan someInterval = TimeSpan.FromMinutes(2);
//sample call
StartTimer(someStart,someInterval,doSomething);
}
//just a fake function to call
private bool doSomething()
{
DialogResult keepGoing = MessageBox.Show("Hey, I did something! Keep Going?","Something!",MessageBoxButtons.YesNo);
return (keepGoing == DialogResult.Yes);
}
//The following is the actual guts.. and can be transplanted to an actual class.
private delegate void voidFunc<P1,P2,P3>(P1 p1,P2 p2,P3 p3);
public void StartTimer(DateTime startTime, TimeSpan interval, Func<bool> action)
{
voidFunc<DateTime,TimeSpan,Func<bool>> Timer = TimedThread;
Timer.BeginInvoke(startTime,interval,action,null,null);
}
private void TimedThread(DateTime startTime, TimeSpan interval, Func<bool> action)
{
bool keepRunning = true;
DateTime NextExecute = startTime;
while(keepRunning)
{
if (DateTime.Now > NextExecute)
{
keepRunning = action.Invoke();
NextExecute = NextExecute.Add(interval);
}
//could parameterize resolution.
Thread.Sleep(1000);
}
}
Another strategy for this would be to record the LAST TIME that the process was run and determine if your desired interval has elapsed since that time. In this strategy, you would code your event to fire if the elapsed time is equal to OR GREATER THAN the desired interval. In this way you can handle instances where long intervals (once per day, for example) could be missed if the computer were to be down for some reason.
So for example:
lastRunDateTime = 5/2/2009 at 8pm
I want to run my process every 24 hours
On a timer event, check whether 24 hours OR MORE passed since the last time the process was run.
If yes, run the process, update lastRunDateTime by adding the desired interval to it (24 hours in this case, but whatever you need it to be)
Obviously, for this to recover after the system has gone down, you will need to store lastRunDateTime in a file or database somewhere so the program could pick up where it left off on recovery.
System.Windows.Forms.Timer (or System.Timers.Timer)
but since now you say you don't want to use Timers, you can run a lightweight wait process on another thread (check time, sleep a few seconds, check time again...) or make a component that raises an event (using a lightweight wait process) on certain scheduled times or intervals
The following should do the trick.
static void Main(string[] Args)
{
try
{
MainAsync().GetAwaiter().GetResult();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
static async Task MainAsync()
{
CancellationTokenSource tokenSource = new CancellationTokenSource();
// Start the timed event here
StartAsync(tokenSource.Token);
Console.ReadKey();
tokenSource.Cancel();
tokenSource.Dispose();
}
public Task StartAsync(CancellationToken cancellationToken)
{
var nextRunTime = new DateTime();
switch (DateTime.Now.AddSeconds(1) < DateTime.Today.AddHours(12)) // add a second to current time to account for time needed to setup the task.
{
case true:
nextRunTime = DateTime.Today.AddHours(12); // Run at midday today.
break;
case false:
nextRunTime = DateTime.Today.AddDays(1).AddHours(12); // Run at midday tomorrow.
break;
}
var firstInterval = nextRunTime.Subtract(DateTime.Now);
Action action = () =>
{
// Run the task at the first interval, then run the task again at midday every day.
_timer = new Timer(
EventMethod,
null,
firstInterval,
DateTime.Today.AddDays(1).AddHours(12).Subtract(DateTime.Now)
);
};
// no need to await this call here because this task is scheduled to run later.
Task.Run(action);
return Task.CompletedTask;
}
private async void EventMethod(object state)
{
// do work
}
My goal is to run an import around 03:00 every night.
Here's my approach, using System.Timers.Timer:
private Timer _timer;
private Int32 _hours = 0;
private Int32 _runAt = 3;
protected override void OnStart(string[] args)
{
_hours = (24 - (DateTime.Now.Hour + 1)) + _runAt;
_timer = new Timer();
_timer.Interval = _hours * 60 * 60 * 1000;
_timer.Elapsed += new ElapsedEventHandler(Tick);
_timer.Start();
}
void Tick(object sender, ElapsedEventArgs e)
{
if (_hours != 24)
{
_hours = 24;
_timer.Interval = _hours * 60 * 60 * 1000;
}
RunImport();
}