QueueBackgroundWorkItem to reliably schedule and run background processes in ASP.NET

Stack Overflow is loaded with questions on how to reliably run a resource-intensive process on a background thread. See  so0, so1, so2, so3, so4, so5, so6, so7, so8, so9, so10 . Examples of long-running tasks include sending email, image processing, and generating a PDF file. When Phil Haack was a program manager on the ASP.NET MVC team, he wrote the definitive blog on the inherent unreliability of running background tasks on ASP.NET. While Phil’s blog is a good read, there are now three supported approaches to launching long-running process on ASP.NET:

  1. Cloud Services worker role is an environment in which you can run code. It’s basically a computer, really. You run whatever code you want (EXE, BAT, PS1, NodeJS, .NET, etc.)  An Azure worker role provides the most industrial strength and scalable solution to this problem.  For an excellent tutorial with this approach, see Tom Dykstra’s Get Started with Azure Cloud Services and ASP.NET.
  2. WebJobs (including the WebJobs SDK) are a way in Azure to run scheduled tasks or tasks that trigger on demand (given various types of triggers). Apps specifically written with the Azure Jobs SDK can be used to run code in any environment, including a local computer, Azure Web Site, Azure Worker Role, Azure VM, etc.  Although you can run them anywhere, they run most efficiently within Azure. For more information see Azure WebJobs – Recommended Resources.
  3. QueueBackgroundWorkItem (QBWI). This was specifically added to enable ASP.NET apps to reliably run short-lived background tasks. (With some limitations explained at the end of this blog.)    As of today, you can’t use QBWI on an Azure Web Site or Cloud Services web role because QBWI requires .Net 4.5.2. We hope to have Azure Web/Cloud running .Net 4.5.2 soon.

In addition to the three supported approaches above, the open source HangFire package allows you to run background tasks.

QueueBackgroundWorkItem overview

QBWI schedules a task which can run in the background, independent of any request. This differs from a normal ThreadPool work item in that ASP.NET automatically keeps track of how many work items registered through this API are currently running, and the ASP.NET runtime will try to delay AppDomain shutdown until these work items have finished executing.

QueueBackgroundWorkItem API

[SecurityPermission(SecurityAction.LinkDemand, Unrestricted =true)]
public static void QueueBackgroundWorkItem(Action<CancellationToken> workItem);

Takes a void-returning callback; the work item will be considered finished when the callback returns.

Takes a Task returning callback; the work item will be considered finished when the returned Task transitions to a terminal state.

Send email with attachment using QBWI

To use QBWI (QueueBackgroundWorkItem) in Visual Studio, you’ll need to install .Net 4.5.2, then install the .Net 4.5.2 Developer Pack. For my sample I created an MVC app and used SendGrid to send an email with a large jpg attachment. To use QBWI, you’ll need to right click the project in solution explore and select Properties. Select the Application tab on the left, then select .Net Framework 4.5.2 in the Target Framework dropdown. If you don’t see 4.5.2, you didn’t install the .Net 4.5.2 Developer Pack or you don’t have .Net 4.5.2 installed.


The following code sends email with an image file attached:

I set the account and password on the Configure tab in the Azure portal to keep my credentials secure.


Using the cancellation token

You can drop the following code in a new MVC app to test the cancellation token:

Hit F5 to debug the app, then click on the About or Contact link. Right click on the IIS Express icon in the task notification area and select Exit.


The visual studio output window shows the task is canceled.


QueueBackgroundWorkItem limitations

  • The QBWI API cannot be called outside of an ASP.NET-managed AppDomain.
  • The AppDomain shutdown can only be delayed 90 seconds (It’s actually the minimum of HttpRuntimeSection.ShutdownTimeout and processModel shutdownTimeLimit). If you have so many items queued that they can’t be completed in 90 seconds, the ASP.NET runtime will unload the AppDomain without waiting for the work items to finish.
  • The caller’s ExecutionContext is not flowed to the work item. For example, the code that you run in the background thread doesn’t have access to commonly used context properties. If you need HttpContext information, copy the values you care about to a state object or inside a closure and pass it in to the background worker.  Don’t pass the HttpContext instance itself, as it’s not a thread-safe object and even simple property getters (like HttpContext.Request.Url) might throw.
  • Scheduled work items are not guaranteed to ever execute, once the app pool starts to shut down, QueueBackgroundWorkItem calls will not be honored.
  • The provided CancellationToken will be signaled when the application is shutting down. The work item should make every effort to honor this token.  If a work item does not honor this token and continues executing, the ASP.NET runtime will unload the AppDomain without waiting for the work item to finish.
  • We don’t guarantee that background work items will ever get invoked or will run to completion.  For instance, if we believe a background work item is misbehaving, we’ll kill it.  And if the w3wp.exe process crashes, all background work items are obviously dead.  If you need reliability, you should use Azure’s built-in scheduling functions.

Special thanks to @LeviBroderick  who not only wrote the QBWI code, but helped me with this post.

Follow me ( @RickAndMSFT )   on twitter where I have a no spam guarantee of quality tweets.


No Comment.