Fire and Forget Async Method in ASP.NET MVC

Fire and forget async method in asp.net mvc

First off, let me point out that "fire and forget" is almost always a mistake in ASP.NET applications. "Fire and forget" is only an acceptable approach if you don't care whether DeleteFooAsync actually completes.

If you're willing to accept that limitation, I have some code on my blog that will register tasks with the ASP.NET runtime, and it accepts both synchronous and asynchronous work.

You can write a one-time wrapper method for logging exceptions as such:

private async Task LogExceptionsAsync(Func<Task> code)
{
try
{
await code();
}
catch(Exception exception)
{
m_log.Error("Call failed: " + exception.ToString());
}
}

And then use the BackgroundTaskManager from my blog as such:

BackgroundTaskManager.Run(() => LogExceptionsAsync(() => DeleteFooAsync()));

Alternatively, you can keep TaskScheduler.UnobservedTaskException and just call it like this:

BackgroundTaskManager.Run(() => DeleteFooAsync());

Fire and Forget with ASP.NET MVC

I know this is an old question, but here's my take on such things, for what it's worth, since I disagree with the accepted answer.

You don't need an AsyncController because you are not interested in waiting for your async operations to complete. So the answer to your question with respect to the MVC side of things is: it doesn't matter. You can do your work any which way and have just a regular old action that kicks off the process and returns whatever result you want.

The second part of your question is really more relevant. You want to make sure nothing is going to happen to your async tasks given that you've started them from your web process, assuming a task itself does not throw an exception. The answer to this depends on your reliability requirements.

You mentioned that you don't want a separate process, and this limits your options. Your tasks will be running in the same app domain with your web application. If anything brings down the app domain or the process, your tasks will die, potentially in a strange state. This isn't necessarily even from unhandled exceptions. IIS can be set to automatically recycle an application from time to time or in certain conditions. Or if you release new code or touch anything in the bin directory, your app domain will be torn down after all requests are finished, and a new one is started. If these cases are a show-stopper for you, then you have no choice but to move your tasks out of process and communicate with some sort of messaging.

If you are not worried about IIS killing you, you still have to worry about yourself. Unhandled exceptions from other background tasks will bring down the process if you don't last-chance handle them with the AppDomain.UnhandledException event. In the case of using the Task Parallel Library, Tasks with exceptions that you don't observe by Waiting on them or viewing the Result or Exception properties will bring down the process if you don't last-chance observe them in the TaskScheduler.UnobservedTaskException event.

A further note is that any ThreadPool threads used for your background operations will not be able to serve requests for your web application during that time. You could manage the max threads in the pool, or instead start a new Thread. Or if you're using TPL with the default scheduler, schedule the task with the LongRunning hint to effectively gain a new thread.

.NET MVC Fire and Forget Method also return View immediately

I'm looking on best solution for Fire & Forget a method at the Action and return View immediately

That depends on what "best" means. Fire and Forget on ASP.NET is inherently unsafe, so there are varying degrees of how safe you want your code to be.

If your app must continue execution, then the only safe system is to have your action handler write what it wants to do into a safe storage mechanism (e.g., Azure Queue, MSMQ, or SQL Server). Once it has been safely stored, then your action method can return. Then you'll also have an independent background process (e.g., Azure Function, Win32 Service, or possibly a thread in your ASP.NET process only if you're very careful about how it's hosted). That background process will read from the safe storage and do the actual work.

If your app is fine with occasionally "losing" work (after returning success to the client), then you can use a less-safe mechanism such as HostingEnvironment.QueueBackgroundWorkItem for .NET 4.5.2, or my AspNetBackgroundTasks library for earlier versions.

Other alternatives are listed in my blog post on the subject.

Correct way to implement fire and forget async method call

In my project, I have a network call to sending Email, I don't want to wait for the response, because the most likely Email provider sends the Emails successfully.

Most email providers work by having you send into a queue, and then the actual email is sent later, when they process work out of that queue. So, sending into the queue is fast and very reliable.

So this means that your SendEmailAsync or whatever API should be quite fast, and shouldn't require returning early. Since SendEmailAsync actually sends to a queue, the only thing it represents is "please accept my request to send this email".

Which of the following methods is better and what is the difference?

Neither.

Since sending emails is just a queue write, and you don't want your request to be lost, the appropriate approach is not to use fire-and-forget at all:

public async Task<IActionResult> Index()
{
await SendAsync();
return View();
}

private async Task SendAsync()
{
_logger.LogInformation("Before");
await Task.Delay(10000); // Email send
_logger.LogInformation("After");
}

If, for some reason, you're using an email provider that doesn't queue, then you can create your own queue (Azure Storage Queue, Amazon SQS, RabbitMQ, etc) and change SendAsync to write a message to that queue. Then have a separate background process (Azure Function, Amazon Lambda, etc) read from that queue and send the emails to the email provider.

what's the best way to invoke Async Code from Non Async in C# (Fire and Forget)

The best way to achieve fire and forget methods are with the async void modifiers:

public async void PersistTask()
{
// no need to assign the task if you are just going to await for it.
await Task.Run(() =>
{
Thread.Sleep(3000);
Console.WriteLine("First");
return Task.FromResult(0);
});
}

And call it using:

//Blocking
public void OrderForm_Load()
{
var t1 = new ServiceTask();

Task.Run(() => t1.PersistTask()); //Approach 1
//[OR]
t1.PersistTask(); //Approach 2
Console.WriteLine("Second");
}

There's another difference, though: Task.Run is best suited for CPU-bound methods. See many more details in this answer.

Also, note that you should avoid using synchronous waits in asynchronous work. So, change this:

Thread.Sleep(3000);

For this:

Task.Delay(3000);

Proper way to start and async fire-and-forget call?

It depends on what you mean by proper :)

For instance: are you interested in the exceptions being thrown in your "fire and forget" calls? If not, than this is sort of fine. Though what you might need to think about is in what environment the task lives.

For instance, if this is a asp.net application and you do this inside the lifetime of a thread instantiated due to a call to a .aspx or .svc. The Task becomes a background thread of that (foreground)thread. The foreground thread might get cleaned up by the application pool before your "fire and forget" task is completed.

So also think about in which thread your tasks live.

I think this article gives you some useful information on that:
https://www.hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx

Also note that if you do not return a value in your Tasks, a task will not return exception info. Source for that is the ref book for microsoft exam 70-483
There is probably a free version of that online somewhere ;P https://www.amazon.com/Exam-Ref-70-483-Programming-C/dp/0735676828

Maybe useful to know is that if your have an async method being called by a non-async and you wish to know its result. You can use .GetAwaiter().GetResult().

Also I think it is important to note the difference between async and multi-threading.

Async is only useful if there are operations that use other parts of a computer that is not the CPU. So things like networking or I/O operations. Using async then tells the system to go ahead and use CPU power somewhere else instead of "blocking" that thread in the CPU for just waiting for a response.

multi-threading is the allocation of operations on different threads in a CPU (for instance, creating a task which creates a background thread of the foreground thread... foreground threads being the threads that make up your application, they are primary, background threads exist linked to foreground threads. If you close the linked foreground thread, the background thread closes as well)
This allows the CPU to work on different tasks at the same time.

Combining these two makes sure the CPU does not get blocked up on just 4 threads if it is a 4 thread CPU. But can open more while it waits for async tasks that are waiting for I/O operations.

I hope this gives your the information needed to do, what ever it is you are doing :)

Async/await with/without awaiting (fire and forget)

I'm asking this, because we're moving our app to service fabric where we no longer can use HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken => await LongMethodAsync()); and the advice is to simply replace it with Task.Run.

That's bad advice. You should use a separate background process separated from your web frontend by a queue.

What's the in-depth logic behind the calls?

  1. Starts the asynchronous method on the current thread. Ignores all results (including exceptions).
  2. Starts the asynchronous method on the current thread. Asynchronously waits for it to complete. This is the standard way of calling asynchronous code.
  3. Starts the asynchronous method on a thread pool thread. Ignores all results (including exceptions).
  4. Starts the asynchronous method on a thread pool thread. Asynchronously waits for it to complete.
  5. Exactly the same as #3.
  6. Exactly the same as #4.


Related Topics



Leave a reply



Submit