c# run an async task in background when parent returns

15,368

Solution 1

I don't really care about the result of the async task, if it fails, it fails.

Really? Just asking, because this is an extremely rare use case.

How can I fire and forget and have it execute in the background so that it does not block the controller from responding to other requests?

I have a blog post that looks into various approaches to "fire and forget" tasks on ASP.NET.

You don't want to just call the method and ignore the returned task, because that method will inherit the request context for the initiating request. Then after the request is completed, the request context is disposed, and that method can end up in an "interesting" place - some things will work, but others will not.

So, the minimum viable solution is to wrap it in Task.Run:

var _ignored = Task.Run(() => DoMethodAsync());

However, note that ASP.NET is completely unaware of this "extra operation". If you want DoMethodAsync to have more of a chance of actually executing to completion, then you'll want to register it with the ASP.NET runtime, either using HostingEnvironment.QueueBackgroundWorkItem (4.5.2+) or my own BackgroundTaskManager.Run (4.5.0/4.5.1).

Also note that none of these options will guarantee that DoMethodAsync will run to completion; they just give you a "best effort." For a truly reliable system, you'd need a proper distributed architecture, i.e., reliable storage with an independent processor.

Solution 2

I know this is old, but after hours of searching and trying different things myself, I came across this thread. Combining some info here with other resource, I was able to implement what I wanted which I believe is the same thing the OP wanted.

I wanted to call a URL like /API/Start and I wanted that URL to instantly return a view that said "Thank you for starting, check back later". However it needed to then simply kick off a background job.

This is with MVC 5 so not sure if it makes a difference, but...

Side Note: To see this in action, use 2 URL's /Default/Index <-- will report back ~5 seconds /Default/IndexAsync <-- will report back ~0 seconds (well sort of .. you'll see)

public class DefaultController : Controller
{

    // GET: Sync
    public ActionResult Index()
    {
        System.Diagnostics.Stopwatch myStopwatch = new System.Diagnostics.Stopwatch();
        myStopwatch.Start();
        DoTask();
        myStopwatch.Stop();

        ViewBag.Message = ($"Process took {myStopwatch.Elapsed.TotalSeconds.ToString()}");
        return View("Index");
    }

    // GET: Async
    public ActionResult IndexAsync()
    {
        System.Diagnostics.Stopwatch myStopwatch = new System.Diagnostics.Stopwatch();
        myStopwatch.Start();
        Task.Factory.StartNew(DoTaskAsync);
        myStopwatch.Stop();

        ViewBag.Message = ($"Process took {myStopwatch.Elapsed.TotalSeconds.ToString()}");
        return View("Index");
    }

    // "the Sync Task"
    private void DoTask()
    {
        System.Diagnostics.Debug.Print($"DoTask Started at {DateTime.Now.ToString()}");
        System.Threading.Thread.Sleep(5000);
        System.Diagnostics.Debug.Print($"DoTask Finished at {DateTime.Now.ToString()}");
    }

    // "the Async Task"
    private async Task DoTaskAsync()
    {
        System.Diagnostics.Debug.Print($"DoTaskAsync Started at {DateTime.Now.ToString()}");
        System.Threading.Thread.Sleep(5000);
        System.Diagnostics.Debug.Print($"DoTaskAsync Finished at {DateTime.Now.ToString()}");
    }      

}
Share:
15,368
WindowsMaker
Author by

WindowsMaker

Updated on June 08, 2022

Comments

  • WindowsMaker
    WindowsMaker almost 2 years

    I am writing a MVC5 app that has a controller with a function say Method1

    I have a third party function that is async Task, let's call it DoMethodAsync (it essentially makes a POST call).

    I want to call it right before returning from Method1 but I don't really care about the result of the async task, if it fails, it fails. How can I fire and forget and have it execute in the background so that it does not block the controller from responding to other requests?

    How can I ensure that DoMethodAsync is only executed after Method1 has returned?