The Dangers of Implementing Recurring Background Tasks In ASP.NET

I like to live life on the wild side. No, I don’t base jump off of buildings or invest in speculative tranches made up of junk stock derivatives. What I do is attempt to run recurring background tasks within an ASP.NET application.

110121-M-2339L-074 Writing code is totally just like this - Photo by DVIDSHUBCC BY 2.0 

But before I do anything wild with ASP.NET, I always talk to my colleague, Levi (sadly, no blog). As a developer on the internals of ASP.NET, he knows a huge amount about it, especially the potential pitfalls. He’s also quite the security guru. As you read this sentence, he just guessed your passwords. All of them.

When he got wind of my plan, he let me know it was evil, unsupported by ASP.NET and just might kill a cat. Good thing I’m a dog person. I persisted in my foolhardiness and suggested maybe it’s not evil, just risky. If so, how can I do it as safely as possible? What are the risks?

There are three main risks, one of which I’ll focus on in this blog post.

  1. An unhandled exception in a thread not associated with a request will take down the process. This occurs even if you have a handler setup via the Application_Error method. I’ll try and explain why in a follow-up blog post, but this is easy to deal with.
  2. If you run your site in a Web Farm, you could end up with multiple instances of your app that all attempt to run the same task at the same time. A little more challenging to deal with than the first item, but still not too hard. One typical approach is to use a resource common to all the servers, such as the database, as a synchronization mechanism to coordinate tasks.
  3. The AppDomain your site runs in can go down for a number of reasons and take down your background task with it. This could corrupt data if it happens in the middle of your code execution.

It’s this last risk that is the focus of this blog post.

Bye Bye App Domain

There are several things that can cause ASP.NET to tear down your AppDomain.

  • When you modify web.config, ASP.NET will recycle the AppDomain, though the w3wp.exe process (the IIS web server process) stays alive.
  • IIS will itself recycle the entire w3wp.exe process every 29 hours. It’ll just outright put a cap in the w3wp.exe process and bring down all of the app domains with it.
  • In a shared hosting environment, many web servers are configured to tear down the application pools after some period of inactivity. For example, if there are no requests to the application within a 20 minute period, it may take down the app domain.

If any of these happen in the middle of your code execution, your application/data could be left in a pretty bad state as it’s shut down without warning.

So why isn’t this a problem for your typical per request ASP.NET code? When ASP.NET tears down the AppDomain, it will attempt to flush the existing requests and give them time to complete before it takes down the App Domain. ASP.NET and IIS are considerate to code that they know is running, such as code that runs as part of a request.

Problem is, ASP.NET doesn’t know about work done on a background thread spawned using a timer or similar mechanism. It only knows about work associated with a request.

So tell ASP.NET, “Hey, I’m working here!”

The good news is there’s an easy way to tell ASP.NET about the work you’re doing! In the System.Web.Hosting namespace, there’s an important class, HostingEnvironment. According to the MSDN docs, this class…

Provides application-management functions and application services to a managed application within its application domain

This class has an important static method, RegisterObject. The MSDN description here isn’t super helpful.

Places an object in the list of registered objects for the application.

For us, what this means is that the RegisterObject method tells ASP.NET that, “Hey! Pay attention to this code here!” Important! This method requires full trust!

This method takes in a single object that implements the IRegisteredObject interface. That interface has a single method:

public interface IRegisteredObject
{
    void Stop(bool immediate);
}

When ASP.NET tears down the AppDomain, it will first attempt to call Stop method on all registered objects.

In most cases, it’ll call this method twice, once with immediate set to false. This gives your code a bit of time to finish what it is doing. ASP.NET gives all instances of IRegisteredObject a total of 30 seconds to complete their work, not 30 seconds each. After that time span, if there are any registered objects left, it will call them again with immediate set to true. This lets you know it means business and you really need to finish up pronto! I modeled my parenting technique after this method when trying to get my kids ready for school.

When ASP.NET calls into this method, your code needs to prevent this method from returning until your work is done. Levi showed me one easy way to do this by simply using a lock. Once the work is done, the code needs to unregister the object.

For example, here’s a simple generic implementation of IRegisteredObject. In this implementation, I simply ignored the immediate flag and try to prevent the method from returning until the work is done. The intent here is I won’t pass in any work that’ll take too long. Hopefully.

public class JobHost : IRegisteredObject
{
    private readonly object _lock = new object();
    private bool _shuttingDown;

    public JobHost()
    {
        HostingEnvironment.RegisterObject(this);
    }

    public void Stop(bool immediate)
    {
        lock (_lock)
        {
            _shuttingDown = true;
        }
        HostingEnvironment.UnregisterObject(this); 
    }

    public void DoWork(Action work)
    {
        lock (_lock)
        {
            if (_shuttingDown)
            {
                return;
            }
            work();
        }
    }
}

I wanted to get the simplest thing possible working. Note, that when ASP.NET is about to shut down the AppDomain, it will attempt to call the Stop method. That method will try to acquire a lock on the _lock instance. The DoWork method also acquires that same lock. That way, when the DoWork method is doing the work you give it (passed in as a lambda) the Stop method has to wait until the work is done before it can acquire the lock. Nifty.

Later on, I plan to make this more sophisticated by taking advantage of using a Task to represent the work rather than an Action. This would allow me to take advantage of task cancellation instead of the brute force approach with locks.

With this class in place, you can create a timer on Application_Start (I generally use WebActivator to register code that runs on app start) and when it elapses, you call into the DoWork method here. Remember, the timer must be referenced or it could be garbage collected.

Here’s a small example of this:

using System;
using System.Threading;
using WebBackgrounder;

[assembly: WebActivator.PreApplicationStartMethod(
  typeof(SampleAspNetTimer), "Start")]

public static class SampleAspNetTimer
{
    private static readonly Timer _timer = new Timer(OnTimerElapsed);
    private static readonly JobHost _jobHost = new JobHost();

    public static void Start()
    {
        _timer.Change(TimeSpan.Zero, TimeSpan.FromMilliseconds(1000));
    }

    private static void OnTimerElapsed(object sender)
    {
        _jobHost.DoWork(() => { /* What is it that you do around here */ });
    }
}

Recommendation

This technique can make your background tasks within ASP.NET much more robust. There’s still a chance of problems occurring though. Sometimes, the AppDomain goes down in a more abrupt manner. For example, you might have a blue screen, someone might trip on the plug, or a hard-drive might fail. These catastrophic failures can take down your app in such a way that leaves data in a bad state. But hopefully, these situations occur much less frequently than an AppDomain shutdown.

Many of you might be scratching your head thinking it seems weird to use a web server to perform recurring background tasks. That’s not really what a web server is for. You’re absolutely right. My recommendation is to do one of the following instead:

  • Write a simple console app and schedule it using Windows task schedule.
  • Write a Windows Service to manage your recurring tasks.
  • Use an Azure worker or something similar.

Given that those are my recommendations, why am I still working on a system for scheduling recurring tasks within ASP.NET that handles web farms and AppDomain shutdowns I call WebBackgrounder (NuGet package coming later)?

I mean, besides the fact that I’m thick-headed? Well, for two reasons.

The first is to make development easier. When you get latest from our source code, I just want everything to work. I don’t want you to have to set up a scheduled task, or an Azure worker, or a Windows server on your development box. A development environment can tolerate the issues I described.

The second reason is for simplicity. If you’re ok with the limitations I mentioned, this approach has one less moving part to worry about when setting up a website. There’s no need to configure an external recurring task. It just works.

But mostly, it’s because I like to live life on the edge.

What others have said

Requesting Gravatar... Marcus McConnell Oct 16, 2011 10:25 AM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
I just finished up a similar background scheduler but I wasn't sure how to handle app domain shutdowns. Thanks for the code. Very helpful!

It would be great if we had a way for ASP.NET apps to register scheduled tasks outside of the web app domain but sandboxed to only access their parent application. Kind of a safe way to allow a medium trust web app to add a windows scheduled task. Just brainstorming...
Requesting Gravatar... David Crowell Oct 16, 2011 10:37 AM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
A proper hosting environment should have a way for you to run scheduled tasks... Dedicated or virtual servers come to mind.

Shared hosting often has a feature to call a specific url at a specified interval, therefore you can do your background task within a ASP.NET request and avoid all the silliness. :)

I don't like my code to live on the edge. I'd rather do an unsupported 100-kilometer bicycle race on gravel roads - doing that next month.

Having your clever ASP.NET hack cause an outage while on said bike race (and not being around to support it) would be bad. :)
Requesting Gravatar... Peter Oct 16, 2011 11:50 AM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
Had to work on something similar, but ended up using "low" level threads and managing the queue manually. The main reason, from what I remember, was to be able to use Monitor.Pulse and Monitor.Wait to allow the threads to sleep and for throttling.

Too bad RegisterObject requires full trust. A workaround is to ping yourself every so often to make sure the pool doesn't shut down.
Requesting Gravatar... haacked Oct 16, 2011 12:51 PM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
This is a scenario I'd love to see better support for within ASP.NET in the future. Some of us have talked about it, but who knows where it'll end up. :)
Requesting Gravatar... Siler Oct 16, 2011 2:02 PM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
Look at nopCommerce implementation of background tasks
Requesting Gravatar... Daniel Marbach Oct 16, 2011 2:39 PM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
Hy
Why don't you use quartz.net? You could easily integrate quartz.net in your application, you then have the power to schedule task in cron like manner or even do more powerful stuff. You would also gain task persistency etc. Quartz.net is really easy to integrate into any IOC container. Happy scheduling

Daniel
Requesting Gravatar... felix Oct 16, 2011 5:03 PM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
What about wcf-wf hosted in IIS?
Requesting Gravatar... Mike Chaliy Oct 16, 2011 5:05 PM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
>> Why don't you use quartz.net?
Because it looks like guy dead few hundreds hears ago...

That's funny, but few weeks ago I also started working on scheduler(https://github.com/chaliy/inline_scheduler) for asp.net :).
Requesting Gravatar... Saeed Neamati Oct 16, 2011 10:03 PM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
I was doing recurring background tasks in 2 applications (running another thread and executing a method every x minutes), and I had no crash till now at all.

Though, seems that I was a lucky guy. I think a better, easier approach is to let a windows server (or another single ASP.NET application) run the recurring task. This is easier to me.

Thanks.
Requesting Gravatar... Kamran Oct 16, 2011 11:42 PM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
Currently this is what I do, but without the fancy app domain handling. I do exactly what David Crowell said, my hosting provider lets me schedule a "hit a URL and report back" task and I pass in a very long token (yah yah, security by obscurity) to help prevent people from just hitting the URL directly. Unfortunately, their username/password method just won't work with all the ways I tried so it has to be open for now. It works pretty good! I get a nightly email with the output of my tasks so I can see quickly if it failed or not.

Maybe if there was a cheap way to do start a background job (i.e. $1/month or something), I'd do that. My task runs between 20s-1min depending on the response time of the 3rd party service I use.
Requesting Gravatar... ms440 Oct 17, 2011 1:07 AM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
Hi Phil,

I’ve also been working on the very similar problem – I need a way to execute some code at pre-scheduled future time for MVC app without having an additional service running.

I’m using cache expiration event with absolute expiration time and the fact that IIS7.5 on x64 does not wait for the first request but is firing Application_Start as soon as the thread pool is recycled or the server restarts.

In Application_Start I’m checking the database to find out the next absolute expiration time. The task itself is executed in the cache expiration event handler inside database transaction, and it also sets up the cache expiration for the next cycle.

This method has certain limitations (IIS7.5, x64, no farm) but it handles the major problem (web.config changed, server went down, etc.) well.

My question: am I missing some use case that makes my approach “dangerous” also?
Requesting Gravatar... rekna Oct 17, 2011 2:31 AM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
What about running Windows Workflow from an ASP.NET application ? Is it susceptible to these dangers as well ?
Requesting Gravatar... Code Dog Oct 17, 2011 4:58 AM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
Some lower priced hosting setups do not allow you to set up scheduled tasks for windows services so this is a handy alternative.
Requesting Gravatar... Daniel Marbach Oct 17, 2011 5:53 AM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
@mike
Actually that's not true. 2.0 beta has been released 2 weeks ago. The project is alive and under futher development...

Daniel
Requesting Gravatar... anon Oct 17, 2011 9:59 AM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
What about async controllers in mvc?
Requesting Gravatar... Jeff Putz Oct 17, 2011 11:28 AM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
I have several background tasks running in POP Forums for MVC. The motivation is simply that it requires no expertise or access to install them, which is fairly important for the mass of people who run in shared environments and such. Maybe it's not ideal, but it "generally" works.

There is the issue that if no one pings the app, it may shut down. That might be true, but if no one is using the app, it won't need to reindex a thread for searching, send e-mail notifications or update the session table. :)
Requesting Gravatar... haacked Oct 17, 2011 1:54 PM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
@Jeff that's exactly my feeling. :)
Requesting Gravatar... Wyatt Barnett Oct 17, 2011 3:41 PM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
Is the 29 hour recycle hard coded? By default app pools will recycle every 1740 minutes, but that is easily reconfigurable. I know we set it to never unless we've got a reason not to.
Requesting Gravatar... Rob Kent Oct 17, 2011 6:51 PM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
I've used a background thread in the past and am about to do it again :) purely because I cannot install a Windows service on my ASP hosting site.

I might just link the process-check to normal user activity, because if nobody is using the site, why should I care that the scheduler has not run?

Thanks for the good tips for making it more resilient though.
Requesting Gravatar... Khalid Abuhakmeh Oct 17, 2011 8:09 PM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
So this might sound foolish, but why doesn't the ASP.Net team just develop a feature in web apps that allows people to access some sort of safe scheduler? Seems like everyone wants to do it, and there is good rationale which you make yourself above.

Not sure how they would do it, but I'm sure there are a lot of smart people at Microsoft. Maybe a worker process that starts when the app pool starts and sits outside of ASP.Net and is always running unless explicitly stopped by your app or IIS Manager?

It would be a killer feature for the next ASP.Net release. Start working your political charm Haack, I believe you can do it.
Requesting Gravatar... Matthew Wills Oct 17, 2011 9:26 PM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
Would it be reasonable to assume that this problem could also impact cache usage with CacheItemUpdateCallback ( msdn.microsoft.com/...)? I would presume that the cache refresh is being done on a background thread...
Requesting Gravatar... Matt Wrock Oct 18, 2011 1:20 AM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
Awesome info regarding HostingEnvironment.RegisterObject. I've been using background threads in asp.net for years and this is the first I have heard of this. This code will definitely be making its way on to RequestReduce soon!
Requesting Gravatar... Chris Moschini Oct 19, 2011 4:07 PM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
Since you work at Microsoft... can you shake the appropriate trees to get RegisterObject() documentation updated and fixed? There are so many places in .Net documentation where you're cruising through things you're CERTAIN are important, and you get useless method descriptions like:

AddSubTask: Adds a subtask.

Or as a more legit example: msdn.microsoft.com/...

"Gets or sets the URL pattern for the route." Great, what do those patterns look like? How about a list of supported syntax, or a link to one, like one would have in a Regex entry? Nope. How about an example of how to call this that's not from pre-beta days? Also no.
Requesting Gravatar... DmitryTheA Oct 20, 2011 7:02 PM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
wow nice thing is that HostingEnvironment.RegisterObject.. but guys you're crazy =) why not just to run timer on windows service..
Requesting Gravatar... Matt Wrock Oct 21, 2011 4:43 PM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
Dmitry,

While there is certainly a place for Windows Services, they are often overkill for alot of scenarios. For example, my OSS project, RequestReducehas a background thread that merges/minifies css and js as well as sprites and optimizes background images. This has to be done in the background in a web app obviously and I want the adoption/migration story to be as painless and friction free as possible. I want adopters to drop my dll in their bin (preferably via nuget) and be done with it. Can you imagine how adoption would plummet if this installed a windows service? Not just the extra overhead but it would also eliminate a large portion of my user base on hosted or cloud solutions. Its also just one more moving part that can break down and complicate troubleshooting.
Requesting Gravatar... Softlion Oct 22, 2011 10:36 PM
# Optimized version
Yo,
thanks for your blog post, it is a question i retained since so a long time !

Here is an optimized version of your code.


public class WebTaskHost : IRegisteredObject
{
private readonly object _lock = new object();
private bool isShuttingDown;

public WebTaskHost()
{
HostingEnvironment.RegisterObject(this);
}

public void Stop(bool immediate)
{
if (isShuttingDown)
return;

lock (_lock)
{
if (isShuttingDown)
return;

isShuttingDown = true;
HostingEnvironment.UnregisterObject(this);
}
}

public void DoWork(Action action)
{
if (isShuttingDown)
return;

lock (_lock)
{
if (isShuttingDown)
return;

action();
}
}
}

Requesting Gravatar... softlion Oct 22, 2011 11:12 PM
# Optimized version 2
[code]
public class WebTaskHost : IRegisteredObject
{
private readonly object _lock = new object();
private bool isShuttingDown, isUnregistered;

public WebTaskHost()
{
HostingEnvironment.RegisterObject(this);
}

public void Stop(bool immediate)
{
if (isShuttingDown)
return;

lock (_lock)
{
if (isShuttingDown)
return;

isShuttingDown = true;

if (immediate)
{
HostingEnvironment.UnregisterObject(this);
isUnregistered = true;
}
}
}

/// <summary>
///
/// </summary>
/// <param name="action"></param>
/// <returns>false if the caller should shut down the work</returns>
public bool DoWork(Action action)
{
if (isShuttingDown && isUnregistered)
return false;

lock (_lock)
{
if (isShuttingDown)
{
if (!isUnregistered)
{
HostingEnvironment.UnregisterObject(this);
isUnregistered = true;
}
return false;
}

action();
}

return true;
}
}
[/code]
Requesting Gravatar... Richard Oct 24, 2011 6:19 AM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
It is worth to take a look at FluentScheduler, I'm using this now for some time and it is really great (package is on NuGet). Can do things like

Schedule<MyTask>().ToRunNow().AndEvery(2).Seconds();

or

Schedule<MyTask>().ToRunNow().AndEvery(1).Months().OnTheFirst(DayOfWeek.Monday).At(3, 0);
Requesting Gravatar... Leonardo Campos Oct 26, 2011 1:44 AM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
Should I use HostingEnvironment.IncrementBusyCount() on this case?

"If the busy count is greater than zero, the hosting application will not time out even if there are no pending requests."
Requesting Gravatar... mike johnson Oct 27, 2011 4:35 AM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
In Azure you could just queue this up to a web worker, but strange that there really is no batch queue service into which you can just plug in a blob of code to execute periodically.

Is there such a thing?
Requesting Gravatar... Chuck Oct 29, 2011 11:46 AM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
Thomas Marquardt wrote about something similar a while back:

[1] blogs.msdn.com/...

[2] blogs.msdn.com/...
Requesting Gravatar... John Haigh Nov 03, 2011 1:39 AM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
I would ask the question: What are you running in the background task i.e. what is the function being performed at a specified interval?

Is there a better way to solve the problem at hand with a solution other than background tasks? Is it possible that using a Service Bus with Workflow might better solve the problem domain?
Requesting Gravatar... Bruce Chapman Nov 20, 2011 8:26 AM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
DotNetNuke has had a scheduler built in for many versions. It can be configured to either request (piggybacks on some hapless users request) or timer modes. Timer modes sounds suspiciously like what you are talking about here. The DotNetNuke implementation has all sorts of smarts for running on only one server in a webfarm scenario, and smarts to pick up and resume dead requests, and provides a handy overridable Scheduler class to hook your work into, so you can build your own tasks. It's not perfect, but it does allow the extension builder a known scheduled service that even the most basic shared hosting implements.

That said, it's still a path to living dangerously for the uninitiated developer for all the reasons listed in your post. But if you're attempting an ASP.NET scheduled service, you could do worse than downloading the DNN source code and watching how the scheduler works.
Requesting Gravatar... Konstantin Tarkus Dec 07, 2011 9:31 AM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
It would be great to have a "background tasks" functionality right within ASP.NET (MVC). So, that switching between different background tasks execution mechanisms would be easier (Azure Worker, Console App, Windows Service, Web App..)
Requesting Gravatar... Professordave Dec 11, 2011 5:53 AM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
Hello,

Problem: we are getting a thread abort exception from a thread started from application_start using thread.start. We are running iis 7.5 with the always running set to true, asp.net mvc 3, .net 4.0

One theory is that because of this explicitly started thread the system thinks that the startup code has hung and therefore aborts the thread.

Is there a difference between "spawning" a thread implicitly using a timer as phil does above, and "spawning" a thread explicitly using thread.start.

In application_start we are doing a thread.start and that thread does some work in an infinite loop.

We are going to try to re-write it with a timer, but there is a reason we did it this way which I will mention later.

We are getting thread abort exception on that thread. It looks like we get this after the amount of time corresponding to the app startup timeout.

It looks like the thread is the restarted automatically, we think, we will do some more testing.

However it seems that this rarely or never happens when run from visual studio, so debugging is not straight forward, so until I add some more logging I do not know whether application_start is being called again or the thread is restarted or what.

The work we are doing in the infinite loop involves a blocking call, so there is no need for a periodic timer.

We are getting messages from an Oracle advanced queue. The oracle .net routines to get messages include a blocking call. Using this blocking call ensures that we get messages when they arrive and that when there are no messages there is no wasted CPU that you get with busy polling.

Since the amount of work is variable, sometimes many messages, sometimes no messages, a periodic timer might cause issues, either we might get a new thread before the first thread completes or not processing messages fast enough, depending on the duration of the timer.

We could re-write the code to use a one shot timer that is called at the end of the function and have the function run once instead of running for ever.

We changed the timeout for the oracle API call from infinite to something much less. We noticed, at least in visual studio, that the thread would never quit if it was currently waiting in the oracle API call.

For example, if I touch the web.config file, so that application_end  gets called and then apllication_start gets called again, I can see a second thread get created, but the first thread is still running. It stays alive until a message arrives, at which point it returns from the API call but then it gets an exception, I think because the thread is not really still alive.


Can anyone help us to understand conceptually what is happening?

Thanks in advance.
Requesting Gravatar... haacked Dec 11, 2011 6:39 AM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
@ProfessorDave probably better to ask this question in StackOverflow and then post a link to the question here. Include some bits of code so we can see exactly what you mean.
Requesting Gravatar... Chris M Dec 17, 2011 10:00 PM
# Lock unnecessary
Not a big fan of the long-lived lock: The code here wraps the task, which may run for a long time, in a lock. In addition to being named vaguely, "_lock", any lock left lying around leaves room for coding mistakes that cause deadlocks.

The good news is the lock is unnecessary, at least so long as the documentation is correct:
msdn.microsoft.com/...

"The object can either complete processing, call the UnregisterObject method, and then return or it can return immediately and complete processing asynchronously before calling the UnregisterObject method."

Can you verify that documentation is correct?
Requesting Gravatar... haacked Dec 18, 2011 1:04 AM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
Without the lock, the Stop method will return immediately, even though your task is still ongoing. This could cause ASP.NET to tear down the app domain prematurely in the middle of your operation. Hence the lock.

In this particular case, if you were to deadlock, the app domain shutdown timeout will expire and it goes down anyways. So no more harm there than w/o the lock. But if your code is properly written to not deadlock (not hard in this case), then your code is more robust and given time to complete its task. Just keep them short!
Requesting Gravatar... Chris M Dec 18, 2011 11:51 AM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
So then the documentation is incorrect? The app will be torn down as soon as you return from the Stop method, rather than only after UnregisterObject() is called as the docs state?
Requesting Gravatar... haacked Dec 18, 2011 11:54 PM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
@Chris the documentation is not incorrect. You're right, if the first time Stop is called, I could return immediately and let Stop continue to do its work. But now I have to deal with the second time Stop is called which does require the method to call UnregisterObject before it's done.

My initial implementation is intended to keep it simple. In effect, I implemented Stop as if it's being called the second time rather than handling these two cases.

A more complex implementation would have the first call to Stop signal the work that it needs to wrap up, while the second call to Stop wraps it up immediately. Of course this assumes that the nature of the work being done is even amenable to that sort of interaction. :)

I might look into that later, but for now, given the nature of the work I do in the background, this felt sufficient.

The part I'm a little unclear on, now that you mention it, is whether the application will try and wait at all for all objects to be unregistered unless you block on one of the Stop calls. If it tried to, that would allow you to simply go asynchronous in both calls of Stop and hope for the best. But as I understand it, this is not the case. I'll double check. :)

Requesting Gravatar... Chris M Dec 20, 2011 2:50 PM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
Thanks for looking into this. When you posted this, I had existing code that did all of the above (handled long-running tasks inside an ASP.Net app with no additional install burden like a Windows Service) - but it did not implement IRegisterObject because frankly, with that documentation, how could it?

I've abstracted the code to a library:
github.com/.../ASP.Net-Long-Running-Interval-Task

Feedback welcome.
Requesting Gravatar... mm Jan 06, 2012 7:00 PM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET

does your soloution work on asp.net web forms too?
or it is just for mvc?
Requesting Gravatar... haacked Jan 06, 2012 9:19 PM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
It works on ASP.NET 4.0 and above. So yes.
Requesting Gravatar... Anthony Feb 21, 2012 11:04 AM
# re: The Dangers of Implementing Recurring Background Tasks In ASP.NET
In the code sample above, is there no need for the SampleAspNetTimer class to ever detect app shutdown?

If the JoBHost surfaced a "Stopped" property it could stop the timer at the right time, but the fact that you haven't done that suggests that it's not needed, and IIS will tear it down anyway.

What do you have to say?

(will show your gravatar)
Please add 3 and 1 and type the answer here: