{"id":464,"date":"2014-06-04T22:36:00","date_gmt":"2014-06-04T22:36:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/webdev\/2014\/06\/04\/queuebackgroundworkitem-to-reliably-schedule-and-run-background-processes-in-asp-net\/"},"modified":"2022-08-08T07:28:32","modified_gmt":"2022-08-08T14:28:32","slug":"queuebackgroundworkitem-to-reliably-schedule-and-run-background-processes-in-asp-net","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/queuebackgroundworkitem-to-reliably-schedule-and-run-background-processes-in-asp-net\/","title":{"rendered":"QueueBackgroundWorkItem to reliably schedule and run background processes in ASP.NET"},"content":{"rendered":"<p>Stack Overflow is loaded with questions on how to reliably run a resource-intensive process on a background thread. See&#160; <a href=\"http:\/\/stackoverflow.com\/questions\/13038823\/how-to-run-long-lasting-process-asynchronously-under-asp-net\" target=\"_blank\" rel=\"noopener\">so0<\/a>, <a href=\"http:\/\/stackoverflow.com\/questions\/118261\/long-running-code-within-asp-net-process\" target=\"_blank\" rel=\"noopener\">so1<\/a>, <a href=\"http:\/\/stackoverflow.com\/questions\/16254567\/asp-net-long-running-task-thread-is-being-aborted-exception\" target=\"_blank\" rel=\"noopener\">so2<\/a>, <a href=\"http:\/\/stackoverflow.com\/questions\/13746411\/concerns-with-long-running-process-on-iis\" target=\"_blank\" rel=\"noopener\">so3<\/a>, <a href=\"http:\/\/stackoverflow.com\/questions\/20708689\/what-to-do-with-long-running-processes-on-a-asp-net-mvc-site\" target=\"_blank\" rel=\"noopener\">so4<\/a>, <a href=\"http:\/\/stackoverflow.com\/questions\/1325054\/long-running-process-that-will-return-a-file\" target=\"_blank\" rel=\"noopener\">so5<\/a>, <a href=\"http:\/\/stackoverflow.com\/questions\/57845\/backgroundworker-thread-in-asp-net\" target=\"_blank\" rel=\"noopener\">so6<\/a>, <a href=\"http:\/\/stackoverflow.com\/questions\/1325718\/using-threadpool-queueuserworkitem-in-asp-net-in-a-high-traffic-scenario\" target=\"_blank\" rel=\"noopener\">so7<\/a>, <a href=\"http:\/\/stackoverflow.com\/questions\/5466673\/whats-the-current-and-recommended-way-to-fire-off-an-asynchronous-thread\" target=\"_blank\" rel=\"noopener\">so8<\/a>, <a href=\"http:\/\/stackoverflow.com\/questions\/1317641\/queue-based-background-processing-in-asp-net-mvc-web-application\" target=\"_blank\" rel=\"noopener\">so9<\/a>, <a href=\"http:\/\/stackoverflow.com\/questions\/1325718\/using-threadpool-queueuserworkitem-in-asp-net-in-a-high-traffic-scenario\" target=\"_blank\" rel=\"noopener\">so10<\/a> . Examples of long-running tasks include sending email, image processing, and generating a PDF file. When <a href=\"https:\/\/twitter.com\/haacked\" target=\"_blank\" rel=\"noopener\">Phil Haack<\/a> was a program manager on the ASP.NET MVC team, he wrote the definitive blog on the inherent unreliability of <a href=\"http:\/\/haacked.com\/archive\/2011\/10\/16\/the-dangers-of-implementing-recurring-background-tasks-in-asp-net.aspx\/\" target=\"_blank\" rel=\"noopener\">running background tasks on ASP.NET<\/a>. While Phil\u2019s blog is a good read, there are now three supported approaches to launching long-running process on ASP.NET:<\/p>\n<ol>\n<li>Cloud Services worker role is an <em>environment<\/em> in which you can run code. It\u2019s basically a computer, really. You run whatever code you want (EXE, BAT, PS1, NodeJS, .NET, etc.)&#160; An Azure worker role provides the most industrial strength and scalable solution to this problem.&#160; For an excellent tutorial with this approach, see Tom Dykstra\u2019s <a href=\"http:\/\/azure.microsoft.com\/en-us\/documentation\/articles\/cloud-services-dotnet-get-started\/\" target=\"_blank\" rel=\"noopener\">Get Started with Azure Cloud Services and ASP.NET<\/a>. <\/li>\n<li><a href=\"http:\/\/www.asp.net\/aspnet\/overview\/developing-apps-with-windows-azure\/azure-webjobs-recommended-resources\" target=\"_blank\" rel=\"noopener\">WebJobs<\/a> (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.&#160; Although you can run them anywhere, they run most efficiently within Azure. For more information see <a href=\"http:\/\/www.asp.net\/aspnet\/overview\/developing-apps-with-windows-azure\/azure-webjobs-recommended-resources\" target=\"_blank\" rel=\"noopener\">Azure WebJobs &#8211; Recommended Resources<\/a>. <\/li>\n<li>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.)&#160;&#160;&#160; As of today, you can\u2019t 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. <\/li>\n<\/ol>\n<p>In addition to the three supported approaches above, the open source <a href=\"http:\/\/hangfire.io\/\" target=\"_blank\" rel=\"noopener\">HangFire<\/a> package allows you to run background tasks.<\/p>\n<h2>QueueBackgroundWorkItem overview<\/h2>\n<p>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 <strong><em>try<\/em><\/strong> to delay <a href=\"http:\/\/odetocode.com\/articles\/305.aspx\" target=\"_blank\" rel=\"noopener\">AppDomain<\/a> shutdown until these work items have finished executing.<\/p>\n<h2>QueueBackgroundWorkItem API<\/h2>\n<p><span style=\"background: white;color: black\">[SecurityPermission(SecurityAction.LinkDemand, Unrestricted =<\/span><span style=\"background: white;color: blue\">true<\/span><span style=\"background: white;color: black\">)]      <br \/><\/span><span style=\"background: white;color: blue\">public static void <\/span><span style=\"background: white;color: black\">QueueBackgroundWorkItem(Action&lt;CancellationToken&gt; workItem);<\/span><\/p>\n<p>Takes a void-returning callback; the work item will be considered finished when the callback returns.<\/p>\n<pre class=\"code\"><span style=\"background: white;color: black\">[SecurityPermission(SecurityAction.LinkDemand, Unrestricted = <\/span><span style=\"background: white;color: blue\">true<\/span><span style=\"background: white;color: black\">)] <br \/><\/span><span style=\"background: white;color: blue\">public static void <\/span><span style=\"background: white;color: black\">QueueBackgroundWorkItem(Func&lt;CancellationToken, Task&gt; workItem);<\/span><\/pre>\n<p>Takes a <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.threading.tasks.task(v=vs.110).aspx\" target=\"_blank\" rel=\"noopener\">Task<\/a> returning callback; the work item will be considered finished when the returned Task transitions to a terminal state.<\/p>\n<h2>Send email with attachment using QBWI<\/h2>\n<p>To use QBWI (QueueBackgroundWorkItem) in Visual Studio, you\u2019ll need to <a href=\"http:\/\/www.microsoft.com\/en-us\/download\/details.aspx?id=42643\" target=\"_blank\" rel=\"noopener\">install .Net 4.5.2<\/a>, then install the <a href=\"http:\/\/www.microsoft.com\/en-us\/download\/details.aspx?id=42637\" target=\"_blank\" rel=\"noopener\">.Net 4.5.2 Developer Pack<\/a>. For my sample I created an MVC app and used <a href=\"http:\/\/sendgrid.com\/\" target=\"_blank\" rel=\"noopener\">SendGrid<\/a> to send an email with a large jpg attachment. To use QBWI, you\u2019ll need to right click the project in solution explore and select <strong>Properties<\/strong>. Select the <strong>Application<\/strong> tab on the left, then select <strong>.Net Framework 4.5.2<\/strong> in the <strong>Target Framework<\/strong> dropdown. If you don\u2019t see 4.5.2, you didn\u2019t install the <a href=\"http:\/\/www.microsoft.com\/en-us\/download\/details.aspx?id=42637\" target=\"_blank\" rel=\"noopener\">.Net 4.5.2 Developer Pack<\/a> or you don\u2019t have .Net 4.5.2 installed.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2014\/06\/3000.netVers_thumb.png\"><img decoding=\"async\" title=\"netVers\" style=\"border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;border-top-width: 0px\" border=\"0\" alt=\"netVers\" src=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2014\/06\/3000.netVers_thumb.png\" width=\"586\" height=\"482\" \/><\/a><\/p>\n<p>The following code sends email with an image file attached:<\/p>\n<pre class=\"code\"><span style=\"background: white;color: black\"> <\/span><span style=\"background: white;color: blue\">public <\/span><span style=\"background: white;color: #2b91af\">ActionResult <\/span><span style=\"background: white;color: black\">SendEmail2([<\/span><span style=\"background: white;color: #2b91af\">Bind<\/span><span style=\"background: white;color: black\">(Include = <\/span><span style=\"background: white;color: #a31515\">&quot;Name,Email&quot;<\/span><span style=\"background: white;color: black\">)] <\/span><span style=\"background: white;color: #2b91af\">User <\/span><span style=\"background: white;color: black\">user)\r\n {\r\n    <\/span><span style=\"background: white;color: blue\">if <\/span><span style=\"background: white;color: black\">(ModelState.IsValid)\r\n    {\r\n       <\/span><span style=\"background: white;color: #2b91af\">HostingEnvironment<\/span><span style=\"background: white;color: black\">.QueueBackgroundWorkItem(ct =&gt; SendMailAsync(user.Email));\r\n       <\/span><span style=\"background: white;color: blue\">return <\/span><span style=\"background: white;color: black\">RedirectToAction(<\/span><span style=\"background: white;color: #a31515\">&quot;Index&quot;<\/span><span style=\"background: white;color: black\">, <\/span><span style=\"background: white;color: #a31515\">&quot;Home&quot;<\/span><span style=\"background: white;color: black\">);\r\n    }\r\n\r\n    <\/span><span style=\"background: white;color: blue\">return <\/span><span style=\"background: white;color: black\">View(user);\r\n }   \r\n\r\n <\/span><span style=\"background: white;color: blue\">private async <\/span><span style=\"background: white;color: #2b91af\">Task <\/span><span style=\"background: white;color: black\">SendMailAsync(<\/span><span style=\"background: white;color: blue\">string <\/span><span style=\"background: white;color: black\">email)\r\n {\r\n    <\/span><span style=\"background: white;color: blue\">var <\/span><span style=\"background: white;color: black\">myMessage = <\/span><span style=\"background: white;color: blue\">new <\/span><span style=\"background: white;color: #2b91af\">SendGridMessage<\/span><span style=\"background: white;color: black\">();\r\n\r\n    myMessage.From = <\/span><span style=\"background: white;color: blue\">new <\/span><span style=\"background: white;color: #2b91af\">MailAddress<\/span><span style=\"background: white;color: black\">(<\/span><span style=\"background: white;color: #a31515\">&quot;Rick@Contoso.com&quot;<\/span><span style=\"background: white;color: black\">);\r\n    myMessage.AddTo(email);\r\n    myMessage.Subject = <\/span><span style=\"background: white;color: #a31515\">&quot;Using QueueBackgroundWorkItem&quot;<\/span><span style=\"background: white;color: black\">;\r\n\r\n    <\/span><span style=\"background: white;color: green\">\/\/Add the HTML and Text bodies\r\n    <\/span><span style=\"background: white;color: black\">myMessage.Html = <\/span><span style=\"background: white;color: #a31515\">&quot;&lt;p&gt;Check out my new blog at &quot;\r\n          <\/span><span style=\"background: white;color: black\">+ <\/span><span style=\"background: white;color: #a31515\">&quot;&lt;a href=&quot;http:\/\/blogs.msdn.com\/b\/webdev\/&quot;&gt;&quot;\r\n          <\/span><span style=\"background: white;color: black\">+ <\/span><span style=\"background: white;color: #a31515\">&quot;http:\/\/blogs.msdn.com\/b\/webdev\/&lt;\/a&gt;&lt;\/p&gt;&quot;<\/span><span style=\"background: white;color: black\">;\r\n    myMessage.Text = <\/span><span style=\"background: white;color: #a31515\">&quot;Check out my new blog at http:\/\/blogs.msdn.com\/b\/webdev\/&quot;<\/span><span style=\"background: white;color: black\">;\r\n\r\n    <\/span><span style=\"background: white;color: blue\">using <\/span><span style=\"background: white;color: black\">(<\/span><span style=\"background: white;color: blue\">var <\/span><span style=\"background: white;color: black\">attachmentFS = <\/span><span style=\"background: white;color: blue\">new <\/span><span style=\"background: white;color: #2b91af\">FileStream<\/span><span style=\"background: white;color: black\">(<\/span><span style=\"background: white;color: #2b91af\">GH<\/span><span style=\"background: white;color: black\">.FilePath, <\/span><span style=\"background: white;color: #2b91af\">FileMode<\/span><span style=\"background: white;color: black\">.Open))\r\n    {\r\n       myMessage.AddAttachment(attachmentFS, <\/span><span style=\"background: white;color: #a31515\">&quot;My Cool File.jpg&quot;<\/span><span style=\"background: white;color: black\">);\r\n    }\r\n\r\n    <\/span><span style=\"background: white;color: blue\">var <\/span><span style=\"background: white;color: black\">credentials = <\/span><span style=\"background: white;color: blue\">new <\/span><span style=\"background: white;color: #2b91af\">NetworkCredential<\/span><span style=\"background: white;color: black\">(\r\n       <\/span><span style=\"background: white;color: #2b91af\">ConfigurationManager<\/span><span style=\"background: white;color: black\">.AppSettings[<\/span><span style=\"background: white;color: #a31515\">&quot;mailAccount&quot;<\/span><span style=\"background: white;color: black\">],\r\n       <\/span><span style=\"background: white;color: #2b91af\">ConfigurationManager<\/span><span style=\"background: white;color: black\">.AppSettings[<\/span><span style=\"background: white;color: #a31515\">&quot;mailPassword&quot;<\/span><span style=\"background: white;color: black\">]\r\n       );\r\n\r\n    <\/span><span style=\"background: white;color: green\">\/\/ Create a Web transport for sending email.\r\n    <\/span><span style=\"background: white;color: blue\">var <\/span><span style=\"background: white;color: black\">transportWeb = <\/span><span style=\"background: white;color: blue\">new <\/span><span style=\"background: white;color: #2b91af\">Web<\/span><span style=\"background: white;color: black\">(credentials);\r\n\r\n    <\/span><span style=\"background: white;color: blue\">if <\/span><span style=\"background: white;color: black\">(transportWeb != <\/span><span style=\"background: white;color: blue\">null<\/span><span style=\"background: white;color: black\">)\r\n       <\/span><span style=\"background: white;color: blue\">await <\/span><span style=\"background: white;color: black\">transportWeb.DeliverAsync(myMessage);\r\n }<\/span><\/pre>\n<p><a href=\"http:\/\/11011.net\/software\/vspaste\"><\/a><\/p>\n<p>I set the account and password on the <strong>Configure <\/strong>tab in the Azure portal to keep my credentials secure.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2014\/06\/1563.conTab_thumb.png\"><img decoding=\"async\" title=\"conTab\" style=\"border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;border-top-width: 0px\" border=\"0\" alt=\"conTab\" src=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2014\/06\/1563.conTab_thumb.png\" width=\"595\" height=\"607\" \/><\/a><\/p>\n<h2>Using the cancellation token<\/h2>\n<p>You can drop the following code in a new MVC app to test the cancellation token:<\/p>\n<pre class=\"code\"><span style=\"background: white;color: blue\">public class <\/span><span style=\"background: white;color: #2b91af\">HomeController <\/span><span style=\"background: white;color: black\">: <\/span><span style=\"background: white;color: #2b91af\">Controller\r\n<\/span><span style=\"background: white;color: black\">{\r\n   <\/span><span style=\"background: white;color: blue\">static int <\/span><span style=\"background: white;color: black\">logCount = 0;\r\n   <\/span><span style=\"background: white;color: blue\">public <\/span><span style=\"background: white;color: #2b91af\">ActionResult <\/span><span style=\"background: white;color: black\">Index()\r\n   {\r\n      <\/span><span style=\"background: white;color: blue\">return <\/span><span style=\"background: white;color: black\">View();\r\n   }\r\n\r\n   <\/span><span style=\"background: white;color: blue\">public <\/span><span style=\"background: white;color: #2b91af\">ActionResult <\/span><span style=\"background: white;color: black\">About()\r\n   {\r\n      ViewBag.Message = <\/span><span style=\"background: white;color: #a31515\">&quot;Your application description page.&quot;<\/span><span style=\"background: white;color: black\">;\r\n      <\/span><span style=\"background: white;color: #2b91af\">HostingEnvironment<\/span><span style=\"background: white;color: black\">.QueueBackgroundWorkItem(ct =&gt; workItemAction1(ct, <\/span><span style=\"background: white;color: #a31515\">&quot;About&quot;<\/span><span style=\"background: white;color: black\">));\r\n      <\/span><span style=\"background: white;color: blue\">return <\/span><span style=\"background: white;color: black\">View();\r\n   }\r\n\r\n   <\/span><span style=\"background: white;color: blue\">public <\/span><span style=\"background: white;color: #2b91af\">ActionResult <\/span><span style=\"background: white;color: black\">Contact()\r\n   {\r\n      ViewBag.Message = <\/span><span style=\"background: white;color: #a31515\">&quot;Your contact page.&quot;<\/span><span style=\"background: white;color: black\">;\r\n      <\/span><span style=\"background: white;color: blue\">string <\/span><span style=\"background: white;color: black\">info = <\/span><span style=\"background: white;color: #a31515\">&quot;Headers: &quot; <\/span><span style=\"background: white;color: black\">+ HttpContext.Request.Headers.AllKeys.ToString()\r\n              + <\/span><span style=\"background: white;color: #a31515\">&quot;  URI: &quot; <\/span><span style=\"background: white;color: black\">+ HttpContext.Request.Url.AbsoluteUri.ToString()\r\n              + <\/span><span style=\"background: white;color: #a31515\">&quot; User: &quot; <\/span><span style=\"background: white;color: black\">+ HttpContext.User.ToString();\r\n\r\n      <\/span><span style=\"background: white;color: #2b91af\">HostingEnvironment<\/span><span style=\"background: white;color: black\">.QueueBackgroundWorkItem(ct =&gt; workItem1Async(ct, <\/span><span style=\"background: white;color: #a31515\">&quot;Contact&quot;<\/span><span style=\"background: white;color: black\">));\r\n      <\/span><span style=\"background: white;color: blue\">return <\/span><span style=\"background: white;color: black\">View();\r\n   }\r\n\r\n   <\/span><span style=\"background: white;color: blue\">private void <\/span><span style=\"background: white;color: black\">workItemAction1(<\/span><span style=\"background: white;color: #2b91af\">CancellationToken <\/span><span style=\"background: white;color: black\">ct, <\/span><span style=\"background: white;color: blue\">string <\/span><span style=\"background: white;color: black\">msg)\r\n   {\r\n      logCount++;\r\n      <\/span><span style=\"background: white;color: blue\">int <\/span><span style=\"background: white;color: black\">currentLogCount = logCount;\r\n\r\n      ct = addLog(ct, currentLogCount, msg);\r\n   }\r\n\r\n   <\/span><span style=\"background: white;color: blue\">private async <\/span><span style=\"background: white;color: #2b91af\">Task<\/span><span style=\"background: white;color: black\">&lt;<\/span><span style=\"background: white;color: #2b91af\">CancellationToken<\/span><span style=\"background: white;color: black\">&gt; workItem1Async(<\/span><span style=\"background: white;color: #2b91af\">CancellationToken <\/span><span style=\"background: white;color: black\">ct, <\/span><span style=\"background: white;color: blue\">string <\/span><span style=\"background: white;color: black\">msg)\r\n   {\r\n      logCount++;\r\n      <\/span><span style=\"background: white;color: blue\">int <\/span><span style=\"background: white;color: black\">currentLogCount = logCount;\r\n\r\n      <\/span><span style=\"background: white;color: blue\">await <\/span><span style=\"background: white;color: black\">addLogAsync(ct, currentLogCount, msg);\r\n      <\/span><span style=\"background: white;color: blue\">return <\/span><span style=\"background: white;color: black\">ct;\r\n   }\r\n\r\n   <\/span><span style=\"background: white;color: blue\">private <\/span><span style=\"background: white;color: #2b91af\">CancellationToken <\/span><span style=\"background: white;color: black\">addLog(<\/span><span style=\"background: white;color: #2b91af\">CancellationToken <\/span><span style=\"background: white;color: black\">ct, <\/span><span style=\"background: white;color: blue\">int <\/span><span style=\"background: white;color: black\">currentLogCount, <\/span><span style=\"background: white;color: blue\">string <\/span><span style=\"background: white;color: black\">msg)\r\n   {\r\n      <\/span><span style=\"background: white;color: #2b91af\">Trace<\/span><span style=\"background: white;color: black\">.TraceInformation(msg);\r\n\r\n      <\/span><span style=\"background: white;color: blue\">for <\/span><span style=\"background: white;color: black\">(<\/span><span style=\"background: white;color: blue\">int <\/span><span style=\"background: white;color: black\">i = 0; i &lt; 5; i++)\r\n      {\r\n         <\/span><span style=\"background: white;color: blue\">if <\/span><span style=\"background: white;color: black\">(ct.IsCancellationRequested)\r\n         {\r\n            <\/span><span style=\"background: white;color: #2b91af\">Trace<\/span><span style=\"background: white;color: black\">.TraceWarning(<\/span><span style=\"background: white;color: blue\">string<\/span><span style=\"background: white;color: black\">.Format(<\/span><span style=\"background: white;color: #a31515\">&quot;{0} - signaled cancellation&quot;<\/span><span style=\"background: white;color: black\">, \r\n               <\/span><span style=\"background: white;color: #2b91af\">DateTime<\/span><span style=\"background: white;color: black\">.Now.ToLongTimeString()));\r\n            <\/span><span style=\"background: white;color: blue\">break<\/span><span style=\"background: white;color: black\">;\r\n         }\r\n         <\/span><span style=\"background: white;color: #2b91af\">Trace<\/span><span style=\"background: white;color: black\">.TraceInformation(<\/span><span style=\"background: white;color: blue\">string<\/span><span style=\"background: white;color: black\">.Format(<\/span><span style=\"background: white;color: #a31515\">&quot;{0} - logcount:{1}&quot;<\/span><span style=\"background: white;color: black\">, \r\n            <\/span><span style=\"background: white;color: #2b91af\">DateTime<\/span><span style=\"background: white;color: black\">.Now.ToLongTimeString(), currentLogCount));\r\n         <\/span><span style=\"background: white;color: #2b91af\">Thread<\/span><span style=\"background: white;color: black\">.Sleep(6000);\r\n      }\r\n      <\/span><span style=\"background: white;color: blue\">return <\/span><span style=\"background: white;color: black\">ct;\r\n   }\r\n\r\n   <\/span><span style=\"background: white;color: blue\">private async <\/span><span style=\"background: white;color: #2b91af\">Task<\/span><span style=\"background: white;color: black\">&lt;<\/span><span style=\"background: white;color: #2b91af\">CancellationToken<\/span><span style=\"background: white;color: black\">&gt; addLogAsync(\r\n      <\/span><span style=\"background: white;color: #2b91af\">CancellationToken <\/span><span style=\"background: white;color: black\">ct, <\/span><span style=\"background: white;color: blue\">int <\/span><span style=\"background: white;color: black\">currentLogCount, <\/span><span style=\"background: white;color: blue\">string <\/span><span style=\"background: white;color: black\">msg)\r\n   {\r\n\r\n      <\/span><span style=\"background: white;color: blue\">try\r\n      <\/span><span style=\"background: white;color: black\">{\r\n         <\/span><span style=\"background: white;color: blue\">for <\/span><span style=\"background: white;color: black\">(<\/span><span style=\"background: white;color: blue\">int <\/span><span style=\"background: white;color: black\">i = 0; i &lt; 5; i++)\r\n         {\r\n            <\/span><span style=\"background: white;color: blue\">if <\/span><span style=\"background: white;color: black\">(ct.IsCancellationRequested)\r\n            {\r\n               <\/span><span style=\"background: white;color: #2b91af\">Trace<\/span><span style=\"background: white;color: black\">.TraceWarning(<\/span><span style=\"background: white;color: blue\">string<\/span><span style=\"background: white;color: black\">.Format(<\/span><span style=\"background: white;color: #a31515\">&quot;{0} - signaled cancellation : msg {1}&quot;<\/span><span style=\"background: white;color: black\">,\r\n                  <\/span><span style=\"background: white;color: #2b91af\">DateTime<\/span><span style=\"background: white;color: black\">.Now.ToLongTimeString(), msg));\r\n               <\/span><span style=\"background: white;color: blue\">break<\/span><span style=\"background: white;color: black\">;\r\n            }\r\n            <\/span><span style=\"background: white;color: #2b91af\">Trace<\/span><span style=\"background: white;color: black\">.TraceInformation(<\/span><span style=\"background: white;color: blue\">string<\/span><span style=\"background: white;color: black\">.Format(<\/span><span style=\"background: white;color: #a31515\">&quot;{0} - msg:{1} - logcount:{2}&quot;<\/span><span style=\"background: white;color: black\">,\r\n               <\/span><span style=\"background: white;color: #2b91af\">DateTime<\/span><span style=\"background: white;color: black\">.Now.Second.ToString(), msg, currentLogCount));\r\n\r\n            <\/span><span style=\"background: white;color: green\">\/\/ &quot;Simulate&quot; this operation took a long time, but was able to run without\r\n            \/\/ blocking the calling thread (i.e., it's doing I\/O operations which are async)\r\n            \/\/ We use Task.Delay rather than Thread.Sleep, because Task.Delay returns\r\n            \/\/ the thread immediately back to the thread-pool, whereas Thread.Sleep blocks it.\r\n            \/\/ Task.Delay is essentially the asynchronous version of Thread.Sleep:\r\n            <\/span><span style=\"background: white;color: blue\">await <\/span><span style=\"background: white;color: #2b91af\">Task<\/span><span style=\"background: white;color: black\">.Delay(2000, ct);\r\n         }\r\n      }\r\n      <\/span><span style=\"background: white;color: blue\">catch <\/span><span style=\"background: white;color: black\">(<\/span><span style=\"background: white;color: #2b91af\">TaskCanceledException <\/span><span style=\"background: white;color: black\">tce)\r\n      {\r\n         <\/span><span style=\"background: white;color: #2b91af\">Trace<\/span><span style=\"background: white;color: black\">.TraceError(<\/span><span style=\"background: white;color: #a31515\">&quot;Caught TaskCanceledException - signaled cancellation &quot; <\/span><span style=\"background: white;color: black\">+ tce.Message);\r\n      }\r\n      <\/span><span style=\"background: white;color: blue\">return <\/span><span style=\"background: white;color: black\">ct;\r\n   }\r\n}<\/span><\/pre>\n<p><a href=\"http:\/\/11011.net\/software\/vspaste\"><\/a><\/p>\n<p>Hit F5 to debug the app, then click on the <strong>About<\/strong> or <strong>Contact<\/strong> link. Right click on the IIS Express icon in the task notification area and select <strong>Exit<\/strong>.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2014\/06\/5102.iisX_thumb.png\"><img decoding=\"async\" title=\"iisX\" style=\"border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;border-top-width: 0px\" border=\"0\" alt=\"iisX\" src=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2014\/06\/5102.iisX_thumb.png\" width=\"241\" height=\"173\" \/><\/a><\/p>\n<p>The visual studio output window shows the task is canceled.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2014\/06\/0827.out_thumb.png\"><img decoding=\"async\" title=\"out\" style=\"border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;border-top-width: 0px\" border=\"0\" alt=\"out\" src=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2014\/06\/0827.out_thumb.png\" width=\"626\" height=\"356\" \/><\/a><\/p>\n<h2>QueueBackgroundWorkItem limitations<\/h2>\n<ul>\n<li>The QBWI API cannot be called outside of an ASP.NET-managed <a href=\"http:\/\/odetocode.com\/articles\/305.aspx\" target=\"_blank\" rel=\"noopener\">AppDomain<\/a>. <\/li>\n<li>The <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/cxk374d9.aspx\" target=\"_blank\" rel=\"noopener\">AppDomain<\/a> shutdown can only be delayed 90 seconds (It&#8217;s actually the minimum of <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.configuration.httpruntimesection.shutdowntimeout(v=vs.110).aspx\">HttpRuntimeSection.ShutdownTimeout<\/a> and processModel <a href=\"http:\/\/www.iis.net\/configreference\/system.applicationhost\/applicationpools\/add\/processmodel\">shutdownTimeLimit<\/a>). If you have so many items queued that they can\u2019t be completed in 90 seconds, the ASP.NET runtime will unload the AppDomain without waiting for the work items to finish. <\/li>\n<li>The caller&#8217;s <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.threading.executioncontext.aspx\" target=\"_blank\" rel=\"noopener\">ExecutionContext<\/a> is not flowed to the work item. For example, the code that you run in the background thread doesn\u2019t have access to commonly used context properties. If you need <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.httpcontext(v=vs.110).aspx\">HttpContext<\/a> information, copy the values you care about to a state object or inside a closure and pass it in to the background worker.&#160; Don\u2019t pass the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.httpcontext(v=vs.110).aspx\">HttpContext<\/a> instance itself, as it\u2019s not a thread-safe object and even simple property getters (like <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.httpcontext.request(v=vs.110).aspx\">HttpContext.Request<\/a>.Url) might throw. <\/li>\n<li>Scheduled work items are not guaranteed to ever execute, once the app pool starts to shut down, QueueBackgroundWorkItem calls will not be honored. <\/li>\n<li>The provided <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.threading.cancellationtoken(v=vs.110).aspx\" target=\"_blank\" rel=\"noopener\">CancellationToken<\/a> will be signaled when the application is shutting down. The work item should make every effort to honor this token.&#160; 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. <\/li>\n<li>We don\u2019t guarantee that background work items will ever get invoked or will run to completion.&#160; For instance, if we believe a background work item is misbehaving, we\u2019ll kill it.&#160; And if the w3wp.exe process crashes, all background work items are obviously dead.&#160; If you need reliability, you should use Azure\u2019s built-in scheduling functions. <\/li>\n<\/ul>\n<p>Special thanks to <a href=\"https:\/\/twitter.com\/LeviBroderick\" target=\"_blank\" rel=\"noopener\">@LeviBroderick<\/a>&#160; who not only wrote the QBWI code, but helped me with this post.<\/p>\n<p>Follow me ( <a href=\"https:\/\/twitter.com\/RickAndMSFT\">@RickAndMSFT<\/a> )&#160;&#160; on twitter where I have a no spam guarantee of quality tweets.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Stack Overflow is loaded with questions on how to reliably run a resource-intensive process on a background thread. See&#160; 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 [&hellip;]<\/p>\n","protected":false},"author":415,"featured_media":58792,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[197],"tags":[7270,108],"class_list":["post-464","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-aspnet","tag-code-sample","tag-performance"],"acf":[],"blog_post_summary":"<p>Stack Overflow is loaded with questions on how to reliably run a resource-intensive process on a background thread. See&#160; 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 [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/464","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/415"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=464"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/464\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58792"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=464"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=464"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=464"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}