Running a background thread inside an ASP.NET website

When most people think of a web site or web application they think of something that respond to user navigations. The site has a life cycle that begins when the call comes in and ends when the response has been returned. Between these users calls the site is dead. But in reality it’s possible to create background threads in an ASP.NET web application that can perform given tasks at certain intervals.

Be careful!
There are several ways of running code in the background on a server. In this example the new thread is run in the background and as such, the ASP.NET application has no control or knowledge about it. If (when) the application shuts down it will terminate the thread right away. You should therefore not have any system critical features in a solution like this.

Code
The following code sample shows a really simple way of doing this. In a static function we create a background thread that initiates a 60 second timer at startup. The timer call will then, every minute, check the log for intrusions (or whatever you want it to do) and take action upon it. The code below should be wrapped in try-catch blocks as well as having other validity checks. It’s being left out just to make the code more simple.

public class BackgroundThread
{
  public static void StartCheckingLog()
  {
    var thread = new Thread(new ThreadStart(StartJob));
    thread.IsBackground = true;
    thread.Name = "BackgroundChecker";
    thread.Start();
  }

  private static void StartJob()
  {
    var logChecker = new LogChecker();
    var timer = new System.Timers.Timer();
    timer.Interval = 60000;
    timer.Elapsed += new System.Timers.ElapsedEventHandler(logChecker.SearchLogForIntrusion);
    timer.Enabled = true;
    // If AutoReset=false then the timer will only tick once
    timer.AutoReset = true;
    timer.Start();
  }

  private class LogChecker
  {
    internal void SearchLogForIntrusion(object sender, System.Timers.ElapsedEventArgs e)
    {
      //      ...some smart checking here...
      Trace.TraceError("Found intrusion in the log...");
    }
  }
}

All we now have to do is to start the background thread and that’s easiest done from the Global.asax.cs file inside the Application_Start() event.

public class MvcApplication : System.Web.HttpApplication
{
  protected void Application_Start()
  {
    AreaRegistration.RegisterAllAreas();
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);

    BackgroundThread.StartCheckingLog();
  }
}

Thanks all you need to do.

One or more threads?
Every time the timer’s interval has elapsed a new call will be made. If the previous call isn’t completed a new thread will be created from the ThreadPool (if one is available). MSDN states the following about the use of the ThreadPool.

If the SynchronizingObject property is null, the Elapsed event is raised on a ThreadPool thread. If processing of the Elapsed event lasts longer than Interval, the event might be raised again on another ThreadPool thread. In this situation, the event handler should be reentrant.

But it is possible for the thread to delay more than the timer interval and still start a new call on the same thread. It all depends on how you’ve implemented your function. Lets take a look at two simple examples.

In the first example we’re using Task.Delay to sleep. Task using the await keyword is not blocking the thread so the thread has the possibility to make a second call while the first one is being “delayed”.

internal async void SearchLogForIntrusion(object sender, System.Timers.ElapsedEventArgs e)
{
  Console.WriteLine("Starting...on thread {0}", System.Threading.Thread.CurrentThread.ManagedThreadId);
  await Task.Delay(3000);
  Console.WriteLine("Ending...on thread {0}", System.Threading.Thread.CurrentThread.ManagedThreadId);
}

In the second example we’re using Thread.Sleep to sleep. This puts the thread to sleep so when the timer has elapsed a new thread has to be used from the threadpool.

internal void SearchLogForIntrusion(object sender, System.Timers.ElapsedEventArgs e)
{
  Console.WriteLine("Starting...on thread {0}", System.Threading.Thread.CurrentThread.ManagedThreadId);
  System.Threading.Thread.Sleep(3000);
  Console.WriteLine("Ending...on thread {0}", System.Threading.Thread.CurrentThread.ManagedThreadId);
}

This might not seem like a big thing, but can be important if you have values that are being shared between the different timer calls.

A note on server usage
If you pay for a cloud service where you pay per minute, or similar, then a solution like this that runs a background thread all the time might not be a cheap solution. It’s all a matter of what solution you have. But many messaging solutions need a thread constantly listening for incoming messages and if you already are paying for such a solution you might as well find it useful to run it inside your web application, as illustrated here.