Exploring QueueBackgroundWorkItem in ASP.NET and Framework 4.5.2
Sometimes it’s very useful for long running tasks that don’t need to complete before returning a response to the user in ASP.NET application.
But before .NET 4.5.2 release , we were not sure that these tasks have been executed safely.
In the release notes, QueueBackgroundWorkItem is summarized as follows:
HostingEnvironment.QueueBackgroundWorkItem lets you schedule small background work items. ASP.NET tracks these items and prevents IIS from abruptly terminating the worker process until all background work items have completed.
The benefit from this is reliably. If you use HostingEnvironment queue in an ASP.NET application, any background tasks are guaranteed to execute safely.
How does QueueBackgroundWorkItem works ?, as follow :
using System.Web.Mvc; using System.Web.Hosting; namespace MyApp.Controllers { public class HomeController : Controller { public ActionResult Index() { HostingEnvironment.QueueBackgroundWorkItem(clt => { //Background task that needs to be performed safely }); return View(); } } }
Note that HostingEnvironment.QueueBackgroundWorkItem belongs to System.Web.Hosting NameSpace.
This method defines two overloads:
Action
Func
Firstly, here are samples with Action overload :
We will define long running actions : a classical Task and an async Task:
using System.Web.Mvc; using System.Web.Hosting; using System.Threading; using System.Threading.Tasks; using System.Diagnostics; namespace MyApp.Controllers { public class HomeController : Controller { public ActionResult Index() { HostingEnvironment.QueueBackgroundWorkItem(clt=> { //Background task that needs to be performed safely }); return View(); } //Action overload's target private void LongRunningAction(CancellationToken clt) { Task.Run(() => { Thread.Sleep(5000); Debug.WriteLine("Action executed"); }); } //Action overload's target private async void LongRunningActionAsync(CancellationToken clt) { await Task.Run(() => { Thread.Sleep(5000); Debug.WriteLine("Action async executed"); }); } } }
Now let’s see ways to use it:
using System.Web.Mvc; using System.Web.Hosting; using System.Threading; using System.Threading.Tasks; using System.Diagnostics; using System; namespace MyApp.Controllers { public class HomeController : Controller { public ActionResult Index() { //Sample 1 //Action overload //with lambda expression HostingEnvironment.QueueBackgroundWorkItem( clt => LongRunningAction(clt) ); //Sample 2 //Action overload //without lambda expression HostingEnvironment.QueueBackgroundWorkItem( (Action)LongRunningAction ); //Sample 3 //Action overload //with lambda expression HostingEnvironment.QueueBackgroundWorkItem( clt => LongRunningActionAsync(clt) ); //Sample 4 //Action overload //without lambda expression HostingEnvironment.QueueBackgroundWorkItem( await (Action)LongRunningAction ); return View(); } //Action overload's target private void LongRunningAction(CancellationToken clt) { Task.Run(() => { Thread.Sleep(5000); Debug.WriteLine("Action executed"); }); } //Action overload's target private async void LongRunningActionAsync(CancellationToken clt) { await Task.Run(() => { Thread.Sleep(5000); Debug.WriteLine("Action async executed"); }); } } }
As you can see, you can execute Actions, with Lambda expression syntax (sample 1) or not (sample 2)
You can also execute async Actions, with Lambda expression syntax (sample 3) or not (sample 4)
Secondly, here are samples with Func
We will define a function that return a long running Task :
using System.Web.Mvc; using System.Web.Hosting; using System.Threading; using System.Threading.Tasks; using System.Diagnostics; namespace MyApp.Controllers { public class HomeController : Controller { public ActionResult Index() { HostingEnvironment.QueueBackgroundWorkItem( clt => { } ); return View(); } //Func overload's target private Task LongRunningFunc(CancellationToken clt) { return Task.Run(() => { Thread.Sleep(5000); Debug.WriteLine("Func executed"); }); } } }
Let’s see ways to use it :
using System.Web.Mvc; using System.Web.Hosting; using System.Threading; using System.Threading.Tasks; using System.Diagnostics; using System; namespace MyApp.Controllers { public class HomeController : Controller { public ActionResult Index() { //Sample 5 //Funcoverload //With lambda expression HostingEnvironment.QueueBackgroundWorkItem( clt => LongRunningFunc(clt) ); //Sample 6 //Func overload //Without lambda expression HostingEnvironment.QueueBackgroundWorkItem( (Func )LongRunningFunc ); //Sample 7 //Func overload //With lambda expression //Accept async / await HostingEnvironment.QueueBackgroundWorkItem( async clt => await LongRunningFunc(clt) ); return View(); } //Func overload's target private Task LongRunningFunc(CancellationToken clt) { return Task.Run(() => { Thread.Sleep(5000); Debug.WriteLine("Func executed"); }); } } }
As you can see, you can execute functions, with Lambda expression syntax (sample 5) or not (sample 6)
You can also use async / await keywords to execute the function (sample 7)
Summary
As you’ve seen, the new QueueBackgroundWorkItem
method is very easy to use with different delegate parameters. ASP.NET does the heavy lifting for us by preventing IIS from terminating worker processes when there are any pending background work items. Consequently, HostingEnvironment.QueueBackgroundWorkItem
is an ideal candidate for scheduling small background jobs in .NET 4.5.2.
I have chosen an ASP.NET MVC for this article, of course, you can call the QueueBackgroundWorkItem
method from another web application type (such as WebForm, and also WCF!), this is not exclusive to MVC.