Dan Harris
Dan Harris

Reputation: 1386

What's the easiest way to schedule a function to run at a specific time using C#

If I had a lot of messages in a database that I wanted to send, and each row specified a date and time to send the message, and a flag for if it has been sent.

These won't always be at fixed intervals, and more than 1 message may want to be sent at the same time.

In this case it would just queue them up and send in order of when they were created.

Is the easiest thing to do just to have a function that runs over and over again, once it completes it just runs again

So it would:

My problem with this is, would it just be horribly inefficient to continuously have a method running, possibly for hours or days without actually sending a message.

The main strain in this case I think would be put on the database, it would constantly be getting hit with a query.

Is there a better way to schedule something like this to happen.

Or just do the above but every time it runs make it wait for 5 minutes before running again.

Does Workflow 4 offer anything suitable for scheduling perhaps?

Upvotes: 5

Views: 2688

Answers (13)

Martin James
Martin James

Reputation: 24857

Many databases allow events to be fired by triggers, eg. 'after insert'. The trigger is run by the database process/thread and the actions it can take are database-specific. It could, for instance, call a C or java procedure that signals a named semaphore upon which you emailer is waiting or exec. an emailer app directly. Look at 'trigger' or 'create trigger' for your database.

Upvotes: 0

Cᴏʀʏ
Cᴏʀʏ

Reputation: 107526

We do this at a financial institution to send out internal e-mails from our intranet applications. Once every 15 minutes, a scheduling software (enterprise scheduler, not a Windows scheduled task) fires off a job. We have a view called PendingEmail on top of a table called EmailQueue that only lists out what needs to be sent this go around (the EmailQueue table has a PopDate, which is an effective date as to when the e-mail should get sent). The application fires off e-mails for whatever it found in the PendingEmails view.

The job sends out a maximum batch size of emails every 15 minutes, marking each record with whether it was successfully sent or whether there was an error (invalid email address, etc.) and what the Exception was, and whether we would like to try re-sending it the next time around. It updates that EmailQueue table all at once, not each record individually. The batch size was put in place to prevent the job from taking more than 15 minutes and stomping on itself.

I don't know that polling every so often is really consuming all that many resources, unless you're going to do it every 5 seconds or something. If you're sending out millions of messages you may need to distribute the work across multiple machines. If you're going to write some custom code, I would use a Timer over Thread.Sleep(), and set the Timer to tick every 5 minutes or whatever interval you'd like to perform work. An event fires on every tick that would subscribe to to start the routine that sends your messages.

See this post on Thread.Sleep() vs. the Timer class:

Upvotes: 0

Binary Worrier
Binary Worrier

Reputation: 51711

Several answers has suggested sending some messages then calling sleep until the next message is due to be sent.

How you sleep in this instance is all important.

You can - in theory - tell a thread to sleep for hours, however if during that time the app (or service) needs to shut down then you're in trouble. The process will be terminated, no cleanup will be executed. This is a less than ideal.

Don't get confused between the concept of polling for work to do, and sleeping between polls.

If you've to wait 5 minutes (or 5 hours) before next polling the database, that's fine, however you never want to *sleep for more than a second or two at a time.

What I'd do . . .

Write a windows service. The service has one active thread that polls the database, see's are any messages due to send, and sends them. It will then poll on a configurable delay (1 minute, 5 minutes, 1 hour, what ever suits).
However it will never sleep for more than a second while it's waiting to poll the database.

If you can be assured that messages can only be added to send after the last message in the DB? If so you can check the time of the next message and not poll until that time.

However, if I find that the next message doesn't need to be sent for 5 hours, is it possible that while I'm waiting a message was added that should be sent in 30 minutes?
If so then you can never trust the "Next message time" and not poll until then, you have to continuously poll on your fixed interval NB worth saying again, your polling interval and your sleep interval are not the same thing.

Upvotes: 1

Paulie Waulie
Paulie Waulie

Reputation: 1690

I recently implemented a windows service which utilized a class called IntervalHeap in the C5 collection class library. I then added a persistence layer which keeps tracks of the items and their intervals in case the service is stopped/crashed.

Has been in production for a few months and has been working very well.

Upvotes: 0

VRK
VRK

Reputation: 400

I would say create a windows service with the timer. it may sleep for configured amount of seconds and then compare the datetime from the database. if it matched then send an e-mail & set the flag in the database for sent e-mails.

Upvotes: 0

twaggs
twaggs

Reputation: 3743

You could use a windows service to accomplish this. Or if you're using MSSQL, you could even use a SQL Server Agent Job.

Upvotes: 1

Glory Raj
Glory Raj

Reputation: 17701

you can use this one .NET Scheduled Timer for checking timeinervals and running the function(sending messages) at specific time intervals ....

Upvotes: 0

Yahia
Yahia

Reputation: 70369

What you describe isn't that bad if you extend it with "when there are no messages due select the next time a message will be due and sleep till then".

Alternatively use a DB with "notification support" making the whole thing event-driven i.e. the DB sends you an event whenever a message is due.

Upvotes: 0

Zruty
Zruty

Reputation: 8667

Whatever method you prefer (make a Windows Service, use Task Scheduler, etc..), please bear in mind that your initial suggestion is exactly what is called busy waiting, which is something you should avoid unless you really know what you're doing.

Upvotes: 0

Digbyswift
Digbyswift

Reputation: 10400

It depends on what you use. Using a scheduled task or a service is perfectly acceptable for the scenario you describe.

You have to be careful though that you do not tie up resources if the process runs too often. It might be more efficient for it to run less often at peak times and more often during off-peak times.

Upvotes: 0

Shyju
Shyju

Reputation: 218732

How about writing a windows service which does this for you. This Windows service will run in the background and check the current time with your db records in a purticular interval (ex : every 5 minutes) and send emails to people and update corresponding records in your tables to set the Email Sent Flag to true

You can even have an SQL job which selects records which are not sent and matches wtih the current time and call a stored procedure which calls dot net assembly to send email . The dot net assembly can use SMTPClient to send emails.

Upvotes: 0

Jean-Bernard Pellerin
Jean-Bernard Pellerin

Reputation: 12670

You could always do a pre-emptive read of the next time value in the series and do a single sleep until then, instead of looping through short sleeps over and over.

Not sure if that's as elaborate as you want though

Upvotes: 2

David
David

Reputation: 218827

Maybe have a compiled view in the database which returns messages that are not sent (I assume there's a flag on each record?) and for which the intended send time is prior to the current time. Then a Windows Service or console application on a scheduled interval can hit that view (which can be performance-tuned in the database pretty well, I'd imagine) and send any messages returned by it.

Upvotes: 1

Related Questions