Click here to Skip to main content
Click here to Skip to main content
Go to top

Task scheduler for websites

, 21 Mar 2008
Rate this:
Please Sign up or sign in to vote.
Create scheduled tasks for your website (no third party or heavy coding involved).

Introduction

This article will help you build a recurring task scheduler that will execute at a specified interval of time. This article overcomes the complexity of scheduling applications that you may find on this site or on internet. This small and easy to understand code overcomes the limitation of installing a Web Service (with special permissions) or a Windows Service or instantiating any other process to run a scheduler. It is safe to run on a shared hosting provider if coded carefully and with the ability to shut down the task if needed.

Background

First of all, let me give a full credit to Michael Taper at angrycoder.com who first wrote the code (the idea behind how this works) in C# back in April 2003. I'm giving credit to this guy since it appears that he wrote that article. I ran into his code in 2008 with ASP.NET 3.5 around, and I I could not make this code run for me in VB.NET. Also, the original coder description on that site was poor in explaining what the code actually does – so I decided to convert his C# code to VB.NET to run on the current .NET version (2.0 or greater as of March 2008) and to bring it down to a level for not so advanced coders.

Using the code

I've placed the whole code in a single file that is constructed like this:

  1. The ISchedulerJob interface
  2. Classes that implement the interface above
  3. Scheduler configuration class
  4. Scheduler engine class

The ISchedulerJob interface is only one line of code that will execute each class that implements it.

Public Interface ISchedulerJob
    Sub Execute()
End Interface

That is plan simple. The Sub Execute() line tells each class that implements it that this call must be defined and implemented for this interface. This ensures the Execute event will fire on each scheduled job. Now, let's get to the second step implementing this interface in our classes. My need for this scheduler was to check the website database and to send an e-mail to users that satisfy a certain criteria. The logic behind this is not included since it goes beyond the scope of this article. As any other scheduler application, this code works by having the jobs created. The job can be anything you wish (anything you can code to execute), but it has to be wrapped in a class that implements the interface above.

The first class, MailJob, actually represents a single job that will be executed on the schedule:

Public Class MailJob 
Implements ISchedulerJob 
 Public Sub Execute() Implements ISchedulerJob.Execute 
   MMCUtil.SendMail("tosomeone@domain.com", _
    "Subject:testing schedule from mail job ",_
    "Body:Body of schedule") 
 End Sub 
End Class

You can see that this class implements the Execute sub of our Job interface, and all it does is call my SendMail function.

The next class is just for demonstration purposes. It represents another job that will be executed at the same time as the MailJob class.

Public Class OtherJob 
Implements ISchedulerJob 
 Public Sub Execute() Implements ISchedulerJob.Execute 
   MMCUtil.SendMail("tosomeone@domain.com", _
    "Subject:testing schedule from other job ", _
    "Body:Body of other job schedule") 
 End Sub 
End Class

As you can see, it does exactly the same as the previous job but the subject and body are different, so it technically is a different job to execute.

You can add as many job classes as you like. The job classes do not have to be executed. To include the job in the the execution schedule, each job class has to instantiated. Later on, I will discuss in more detail how to achieve this.

The next step is to build the configuration class. The configuration has two properties: an integer that keeps the time (in milliseconds) that you wish to wait before the scheduled jobs execute, and an ArrayList that keeps the jobs to be executed.

Public Class SchedulerConfiguration
    Private m_sleepInterval As Integer
    Private m_jobs As New ArrayList()

    Public ReadOnly Property SleepInterval() As Integer
        Get
            Return m_sleepInterval
        End Get
    End Property

    Public ReadOnly Property Jobs() As ArrayList
        Get
            Return m_jobs
        End Get
    End Property

    Public Sub New(ByVal newSleepInterval As Integer)
        m_sleepInterval = newSleepInterval
    End Sub
End Class

The scheduler engine is the one that makes the jobs run. This is where you will wan to pay some attention (design your code carefully), since the jobs are recurring and the execution is placed in an indefinite loop.

Public Class Scheduler 
    Private configuration As SchedulerConfiguration = Nothing 

    Public Sub New(ByVal config As SchedulerConfiguration) 
        configuration = config 
    End Sub 

    Public Sub Start() 
        While True 
            Try 
                For Each job As ISchedulerJob In configuration.Jobs 
                    job.Execute() 
                Next 
            Catch
            Finally 
                Thread.Sleep(configuration.SleepInterval) 
            End Try 
        End While
    End Sub 
End Class

If you wish to execute the jobs only once at a specific time, then simply do not use the while/wend loop and make sure you place a check if the current time (date.Now) is greater than the scheduled time. In this case, you may also wish to set the schedule interval to seconds or minutes. That is all it takes to make the scheduler. To add jobs and execute them, you need to do the following:

  1. Create a new instance of the scheduler configuration (in my case, 1 hour)
  2. Create the jobs by creating a new instance of each job in the scheduler configuration
  3. Create new instance of the scheduler using the above config
  4. Create a new thread that will start with the sub named Start in the scheduler class
  5. Start the newly created thread
Private schedulerThread As System.Threading.Thread = Nothing 

Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
    Dim config As New MailScheduler.SchedulerConfiguration(60 * 60 * 1000)
    config.Jobs.Add(New MailScheduler.MailJob())
    config.Jobs.Add(New MailScheduler.OtherJob())
    Dim scheduler As New MailScheduler.Scheduler(config)
    Dim mailThreadStart As _
    New System.Threading.ThreadStart(AddressOf scheduler.Start)
    schedulerThread = New System.Threading.Thread(mailThreadStart)
    schedulerThread.Start()
End Sub

Sub Application_End(ByVal sender As Object, ByVal e As EventArgs)
    If Not IsNothing(schedulerThread) Then
        schedulerThread.Abort()
    End If
End Sub

I've placed the above code in the global.asax on the application start with the schedulerThread declared as a private variable within the scope of global.asax. The is because I will need to reference it on application end to kill the same thread. That would be a perfect place for any scheduled task to run and then just continue to the site. Now, as the site is working, our jobs will execute in a separate thread that sleeps between the job executions for a specified period of time.

If you wish the code to run as long as the ASP.NET worker process is alive, leave the scheduler thread (do not abort it). It is dangerous to leave any thread open, especially on a hosting provider, as it may cause undesired results. So be warned. The safer method is to create a public reference to the scheduler thread so you can reference it from within a page and abort it by simply loading the page (on the page load event). The approach on how to start and kill the thread is up to you. This article's intention was to give a simple code for implementing the scheduler functionality for recurring tasks. You may wish to search this site for more samples on creating safe threading models for your website.

Happy coding...

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Goran Bacvarovski
Architect
Canada Canada
About 20 years in the software industry - started with simple games programming for the at the time popular Commodore64 and Sinclair Z48, using basic. Moved along with VBA and Office programming macros for all kidns of automations in Office Applications including complex Databases and complete Software solutions developed in MS Access and SQL server later on. Visual Stuidio was native choice for my sofware developement career. Kept loyal to VB as language even though I am verse in other programming languages - the nature of my work in the last 8 years (software architect) keeps pushing toward VB as the most easiest to teach or broadcast the idea with.
Today I manage a .NET team of developers, business analysts, technical writters and atchitects; with applications that not only talk about but do implement enterprise software solutions with large number connections and processing power. I have been working in a capacity as a lead .NET architect for the last 3 years.

Comments and Discussions

 
GeneralUse online services PinmemberKikoz6822-Oct-09 8:11 
GeneralRe: Use online services Pinmembermaxxnostra23-Oct-09 15:21 
GeneralRe: Use online services PinmemberKikoz6826-Oct-09 5:17 
GeneralRe: Use online services Pinmembermaxxnostra27-Oct-09 6:24 
GeneralRe: Use online services PinmemberKikoz6827-Oct-09 9:02 
GeneralRe: Use online services Pinmembermaxxnostra28-Oct-09 4:10 
Generalit works only for a period of time... Pinmembergabriele.galeazzi25-Mar-08 23:32 
AnswerRe: it works only for a period of time... Pinmembermaxxnostra26-Mar-08 11:35 
Remember that the scheduler is beeing instantiated on Application Start and Killed at Application End. This means it will start as soon as the first visitor comes to the site and will shut down as soon as there are no visitors. Followin code ads one more class and lets you control when you want to kill you thread from any web page within the application.
 
Add the following class to your MailScheduler.vb
 
Public Class InitScheduler
Public schedulerThread As System.Threading.Thread = Nothing
Public Sub StartSchedule()
Dim config As New MailScheduler.SchedulerConfiguration(5 * 60 * 1000)
config.Jobs.Add(New MailScheduler.MailJob())
' config.Jobs.Add(New MailScheduler.OtherJob())
Dim scheduler As New MailScheduler.Scheduler(config)
Dim mailThreadStart As New System.Threading.ThreadStart(AddressOf scheduler.Start)
mailthreadStart.IsBackground = True
schedulerThread = New System.Threading.Thread(mailThreadStart)
schedulerThread.Start()
End Sub
 
Public Sub EndSchedule()
If Not IsNothing(schedulerThread) Then
schedulerThread.Abort()
End If
End Sub
End Class

 
Add this code to any page (say page load event) that you want to use to Kill the thread.
 
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
Dim sch As MailScheduler.InitScheduler = Application("iSchedule")
sch.EndSchedule()
If Not IsNothing(sch.schedulerThread) Then
If sch.schedulerThread.IsAlive Then
sch.schedulerThread.Abort()
End If
End If
Session.Abandon()
End Sub

 
Remove all the code from global.asax on application start and application end and use this instead:
 
Public iSchedule As New MailScheduler.InitScheduler 'Global Variable
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
iSchedule.StartSchedule()
Application("iSchedule") = iSchedule
End Sub

 
Hopefully this will work for you. The process will continue to run and you application will be kept alive as long as this backround process is running
QuestionRe: it works only for a period of time... Pinmembergabriele.galeazzi26-Mar-08 22:56 
AnswerRe: it works only for a period of time... Pinmembermaxxnostra27-Mar-08 10:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140916.1 | Last Updated 21 Mar 2008
Article Copyright 2008 by Goran Bacvarovski
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid