Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I raise an event every hour (or specific time interval each hour) in .NET?

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);
}
like image 942
Dan Herbert Avatar asked Nov 21 '08 03:11

Dan Herbert


3 Answers

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;  
like image 150
tvanfosson Avatar answered Oct 10 '22 06:10

tvanfosson


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);
        }            
    }
like image 3
ExCodeCowboy Avatar answered Oct 10 '22 05:10

ExCodeCowboy


You may find help from Quartz.net http://quartznet.sourceforge.net/

like image 11
suhair Avatar answered Oct 10 '22 05:10

suhair