Fire and forget async method in ASP.NET MVC - c#

The general answers such as here and here to fire-and-forget questions is not to use async/await, but to use Task.Run or TaskFactory.StartNew passing in the synchronous method instead. However, sometimes the method that I want to fire-and-forget is async and there is no equivalent sync method.
Update Note/Warning: As Stephen Cleary pointed out below, it is dangerous to continue working on a request after you have sent the response. The reason is because the AppDomain may be shut down while that work is still in progress. See the link in his response for more information. Anyways, I just wanted to point that out upfront, so that I don't send anyone down the wrong path.
I think my case is valid because the actual work is done by a different system (different computer on a different server) so I only need to know that the message has left for that system. If there is an exception there is nothing that the server or user can do about it and it does not affect the user, all I need to do is refer to the exception log and clean up manually (or implement some automated mechanism). If the AppDomain is shut down I will have a residual file in a remote system, but I will pick that up as part of my usual maintenance cycle and since its existence is no longer known by my web server (database) and its name is uniquely timestamped, it will not cause any issues while it still lingers.
It would be ideal if I had access to a persistence mechanism as Stephen Cleary pointed out, but unfortunately I don't at this time.
I considered just pretending that the DeleteFoo request has completed fine on the client side (javascript) while keeping the request open, but I need information in the response to continue, so it would hold things up.
So, the original question...
for example:
//External library
public async Task DeleteFooAsync();
In my asp.net mvc code I want to call DeleteFooAsync in a fire-and-forget fashion - I don't want to hold up the response waiting for DeleteFooAsync to complete. If DeleteFooAsync fails (or throws an exception) for some reason, there is nothing that the user or the program can do about it so I just want to log an error.
Now, I know that any exceptions will result in unobserved exceptions, so the simplest case I can think of is:
//In my code
Task deleteTask = DeleteFooAsync()
//In my App_Start
TaskScheduler.UnobservedTaskException += ( sender, e ) =>
{
m_log.Debug( "Unobserved exception! This exception would have been unobserved: {0}", e.Exception );
e.SetObserved();
};
Are there any risks in doing this?
The other option that I can think of is to make my own wrapper such as:
private void async DeleteFooWrapperAsync()
{
try
{
await DeleteFooAsync();
}
catch(Exception exception )
{
m_log.Error("DeleteFooAsync failed: " + exception.ToString());
}
}
and then call that with TaskFactory.StartNew (probably wrapping in an async action). However this seems like a lot of wrapper code each time I want to call an async method in a fire-and-forget fashion.
My question is, what it the correct way to call an async method in a fire-and-forget fashion?
UPDATE:
Well, I found that the following in my controller (not that the controller action needs to be async because there are other async calls that are awaited):
[AcceptVerbs( HttpVerbs.Post )]
public async Task<JsonResult> DeleteItemAsync()
{
Task deleteTask = DeleteFooAsync();
...
}
caused an exception of the form:
Unhandled Exception: System.NullReferenceException: Object reference
not set to an instance of an object. at System.Web.ThreadContext.AssociateWithCurrentThread(BooleansetImpersonationContext)
This is discussed here and seems to be to do with the SynchronizationContext and 'the returned Task was transitioned to a terminal state before all async work completed'.
So, the only method that worked was:
Task foo = Task.Run( () => DeleteFooAsync() );
My understanding of why this works is because StartNew gets a new thread for DeleteFooAsync to work on.
Sadly, Scott's suggestion below does not work for handling exceptions in this case, because foo is not a DeleteFooAsync task anymore, but rather the task from Task.Run, so does not handle the exceptions from DeleteFooAsync. My UnobservedTaskException does eventually get called, so at least that still works.
So, I guess the question still stands, how do you do fire-and-forget an 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());

As of .NET 4.5.2, you can do the following
HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken => await LongMethodAsync());
But it only works within ASP.NET domain
The HostingEnvironment.QueueBackgroundWorkItem method 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. This method can't be called
outside an ASP.NET managed app domain.
More here: https://msdn.microsoft.com/en-us/library/ms171868(v=vs.110).aspx#v452

The best way to handle it is use the ContinueWith method and pass in the OnlyOnFaulted option.
private void button1_Click(object sender, EventArgs e)
{
var deleteFooTask = DeleteFooAsync();
deleteFooTask.ContinueWith(ErrorHandeler, TaskContinuationOptions.OnlyOnFaulted);
}
private void ErrorHandeler(Task obj)
{
MessageBox.Show(String.Format("Exception happened in the background of DeleteFooAsync.\n{0}", obj.Exception));
}
public async Task DeleteFooAsync()
{
await Task.Delay(5000);
throw new Exception("Oops");
}
Where I put my message box you would put your logger.

Related

What is the proper usage of JoinableTaskFactory.RunAsync?

I searched online but there is very little information regarding ThreadHelper.JoinableTaskFactory.RunAsync
If I have the following code, Test1 runs on MainThread:
public bool Test1()
{
// Do something here
ThreadHelper.JoinableTaskFactory.RunAsync(this.Test2);
// Do something else
return false;
}
private async Task Test2()
{
await TaskScheduler.Default;
// do something here
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
// do something here
}
Is it ok if the RunAsync has never been awaited on? What would happen if Test1 returns before Test2 finishes running?
Is it ok if the RunAsync has never been awaited on?
That depends. It's OK from JoinableTaskFactory's perspective. All the necessary continuations will continue -- it's just that your calling method won't wait for it to complete, which is the whole point of not awaiting it if you choose to do so.
But in general, it may not be healthy for your app. Consider the case that your async work is saving a file (or transmitting something over the network) and the user closes your app. Wouldn't you want the app to wait for it to finish before closing? As #GrzegorzSmulko said in his answer, the JoinableTaskFactory prescribes a pattern for blocking on shutdown (or disposal of your object) to ensure async work completes.
There's another reason to track your async work if you're in an app that hosts the CLR and shuts it down before exiting: you don't want managed threads running around doing arbitrary things when the AppDomain is being finalized or you'll find that your app crashes on shutdown. This is not a concern when you have a pure-managed app though, since it just exits without shutting down the CLR. It will not crash, but it will still abandon any half-done work.
All the foregoing is true in any app that you use JoinableTaskFactory for. If you happen to be using it within Visual Studio (I'm speaking generally here for a broader audience... I know your question specifically mentioned VS) then the rules are stressed more. You should track all your async work as prescribed in that section. You shouldn't have any "fire and forget" work.
The FileAndForget extension method is actually intended for internal Microsoft use since it sends errors to our telemetry servers. If you really want to just forget stuff, you can use the .Forget() extension method. But remember you should only use that after scheduling the work using an AsyncPackage.JoinableTaskFactory instance or another one that is tracking your async work for disposal. Don't use it on ThreadHelper.JoinableTaskFactory because that doesn't track async-and-forgotten work. So for example, don't do this:
ThreadHelper.JoinableTaskFactory.RunAsync(async () => { /* something async */ }).Forget();
The problem with the above is that the async work will not be tracked, and thus not block shutdown. You should do this instead:
myAsyncPackage.JoinableTaskFactory.RunAsync(async () => { /* something async */ }).Forget();
Or even better: just await the call, in which case you can use pretty much any JTF instance:
await ThreadHelper.JoinableTaskFactory.RunAsync(async () => { /* something async */ });
But if you're in a context where you can use await, you often don't need JoinableTaskFactory.RunAsync at all, since if you can just await the code within the delegate itself. Some uncommon scenarios may require that you still track the async work with a JoinableTaskCollection where you might want to use await someJtf.RunAsync but normally you can just drop JTF use where you can naturally await your work.
According to Threading Cookbook for Visual Studio you should use ThreadHelper.JoinableTaskFactory.RunAsync() together with FileAndForget().
The potential problem is, that FileAndForget() is not available in VS2015, but only in VS2017+.
Is it ok if the RunAsync has never been awaited on?
I think it's not ok, you should use FileAndForget. But, I don't really know what to do for VS2015.
What would happen if Test1 returns before Test2 finishes running?
This should be pretty easy to test to make sure. I assume that Test2 will just finish later "But you also should be sure your async work finishes before your object claims to be disposed."

Cancelling async uploading task

I've got an Uploaderclass with one method -Upload
public static int Upload(string endpoint,object objectToBeUploaded)
{
Source.Token.ThrowIfCancellationRequested();
var repos = new UploadRepository(endpoint);
return repos.Upload(objectToBeUploaded);
}
The Source is a static CancellationTokenSource available in the project.
I also have a list of endpoints I need to upload a certain object for.
The code in the Form (it's a very small project using WinForms) looks like this:
private async Task UploadObjectAsync(
string endpoint,
object objectToBeUploaded)
{
try
{
int elementId = await Task.Factory.StartNew(
() => Uploader.Upload(endpoint,objectToBeUploaded));
//do something with the returned value..
}
catch(OperationCanceledEception ex)
{
//handle the exception..
}
}
And then I set the btnUpload.Click handler like this so I can later use it:
this.btnUpload.Click += async (s, e) =>
{
foreach(var endpoint in endpoints)
{
await UploadObjectASsync(endpoint,someObject);
}
}
The problem is that whenever I start uploading to all the endpoints(how they are obtained is irrelevant) and I decide to cancel the uploading process using Source.Cancel(); the first UploadObjectAsyncwill always go through since
the Source.Token.ThrowIfCancellationRequested(); check in the Upload method has already been passed. The rest of tasks will be cancelled normally and handled gracefully.
How am I to restructure this code in order to make sure that the first UploadObjectAsync Task will also be cancelled?
It is worth mentioning that I also don't have access to the source code of the uploading process itself (service reference) - the repos.Upload(objectToBeUploaded) in my Upload method.
You need to make your UploadRepository.Upload take a CancellationToken.
Specially when that's the one doing the I/O operation.. That's when the async/await really pays-off.
That will also help you get rid of that: Task.Factory.StartNew since the Upload method will return Task already. There will be no need to spin off a task.
In your current setup, given enough time for the tasks to start (and go through your ThrowIfCancellationRequested) you won't be able to cancel any upload. Even if it takes 30 seconds.
Also, you might be interested in: Task.Run
There isn't anything practical you can do. The Upload method doesn't take a token. The first task has already passed the cancelation check by the time you hit the cancel button. You can prove to yourself the cancel is a timing issue by adding a 10 second sleep ahead of throw if cancelled call. All tasks would then cancel.
The problem is that you can't stop the process that happens inside the Upload function unless it checks for the state of the CancellationToken any terminates itself.
So what you could do is to abort the thread that is executing by doing something like this:
int elementId = await Task.Factory.StartNew(() =>
{
try
{
using (Source.Token.Register(Thread.CurrentThread.Interrupt))
{
return Uploader.Upload(endpoint, objectToBeUploaded));
}
}
catch (ThreadInterruptedException ex)
{
throw new OperationCanceledEception(ex)
}
}, Source.Token);
Using the Source.Token.Register(delegate) function you cause the token to call that function in case the token is cancelled. This way the thread that is currently executing the the uploaded and should throw a exception right away.
This method only works in case the thread enters the WaitSleepJoin-State from time to time, because the exception is only raised in case the thread is in that state. Have a look at the documentation of the Thread.Interrupt function.
The alternative is to use Thread.Abort and the ThreadAbortedException. This will kill your thread in any case, but it may corrupt the internal state of your service, because locks the thread holds won't be released properly. So be very careful using this method.

Asynchronous call from Application_Error()

I am trying to figure out how to create an asynchronous call from Application_Error Method in MVC.
I have an e-mail dll that I have written which sends out an e-mail via asynchronous call using Smtp Client.
The problem is that when I call SendEmail method in my dll I have conflict of needing to await for the call. Now if I try something like this:
protected async void Application_Error()
{
var await emailService.SendEmail("Hello World");
}
It will not work, the method will never even be called.
Is there a way for me to make an asynchronous call from a synchronous method? My research online hinted at HttpServerUtility.TransferRequest possibly being the answer but I could not figure out how to make it work with my request, it just doesn't make sense to me at the moment.
For those that may encounter this issue in the future, here is another solution that may be a bit more robust. Instead of using ThreadPool.QueueUserWorkItem() you can use HostingEnvironment.QueueBackgroundWorkItem(). Doing things this way, asks the server to make a bit more of an effort to complete these tasks before shutting down.
Here's an example from a recent project:
void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
HostingEnvironment.QueueBackgroundWorkItem(ct =>
ExceptionLogger.LogAsync(ex));
}
limitation of the dll, i cannot change it.
I don't know why that async call does not send an email. It should. There's a bug somewhere.
But a workaround is:
Task.Run(async () => { await emailService.SendEmail("Hello World"); }).Wait();
You could simplify and optimize this but this is the simplest way to bring the idea across. This pattern is always a safe way to synchronously call an async method. Not good for perf, but does not matter here because sending an email is 1000x more expensive than the overhead.
If you want to keep the async behavior you could remove the Wait. Although at that point I wonder why sending is not working in the first place. Is the bug triggered by the async behavior or is it always there?!

Akavache's GetObject<T> hangs when awaited. Any idea what is wrong here?

I have a Xamarin.Forms application, with this code in my App class (yes, this is just a sample to demonstrate the issue):
public App()
{
BlobCache.ApplicationName = "MyApp";
BlobCache.EnsureInitialized();
// The root page of your application
MainPage = GetMainPage();
}
public object BlockingGetExternalUser()
{
return GetExternalUser().Result;
}
private async Task<object> GetExternalUser()
{
try
{
return await BlobCache.LocalMachine.GetObject<object>("user");
}
catch (KeyNotFoundException)
{
return null;
}
}
The Key "user" does not exist, so I would expect to get a KeyNotFoundException. However I never see this exception being thrown. Instead it just "hangs" and never returns from the await GetObject call.
I am running this on my phone with Android 5.0.
Any ideas how to fix this? Am I doing something fundamentally wrong?
Update: On a side note: Instead of immediately trying GetObject, one could try to check if the key actually exists in the cache and only then retrieve it from the cache. However, if I am not mistaken, there is no other way to do a check other than calling GetObject and catching the exception like in the sample above. For a scenario where one would just want to know if an item exists, that doesn't seem to be ideal. Maybe an "Exists()" method would be a nice to have in Akavache? Or maybe I am missing something?
Update2: Changing the example to not use an async method in the constructor. Just to prove a point that that is not the issue.
Update3: Removing the call from the constructor. When I call BlockingGetExternalUser from anywhere in my code, the await will still hang.
You're most certainly having a dead-lock. Quoting Synchronously waiting for an async operation, and why does Wait() freeze the program here:
The await inside your asynchronous method is trying to come back to the UI thread.
Since the UI thread is busy waiting for the entire task to complete, you have a > deadlock.
Note that your call to .Result implies a Task.Wait() somewhere.
There are two solutions: Either completely avoid the async methods, or wrap your code into a Task.Run like this:
public object BlockingGetExternalUser()
{
return Task.Run<object>(() => GetExternalUser().Result);
}
(I hope it's compiling I didn't verify in VS :)
By experience I tend to avoid async methods in combination with SQLite these days. Reason is that most SQLite wrapper libraries use the Task.Run anti-pattern to provide async wrappers around their methods while SQLite doesn't have any intrinsic notations of being asynchronous. Note though that it's perfectly fine for you to wrap things into Task.Run to make them asynchronous and that it's only an anti-pattern for library designers, suggesting to their users that methods are asynchronous when they're actually not. You can read more about this here: Task.Run as an anti-pattern?
Using async methods in a constructors (var externalUser = GetExternalUser().Result;) is considered as a bad code. You shouldn't use async methods in a class constructors. Read this: Can constructors be async?
You could try to change it to avoid deadlocks:
Func<Task> task = async () => { await GetExternalUser().ConfigureAwait(false); };
task().Wait();
... but I won't recommend it.

Task.Run() does not runs asynchronously

I have an ASP.NET MVC 3 site that connects to a WCF service. The WCF Service is independent from the site and is hosted in a Windows Service.
Most of the calls are synchronous, so it's not a problem to wait for the WCF to do it's thing.
However, one of those (already implemented) calls takes a bit too long, and, as it essentially does not output anything directly, I wanted to spin it on the service and forget about it.
So I changed my code from:
public ViewResult StartSlowCalculation(CalculationOptions calculationOptions)
{
WcfServiceProxy.DoSlowCalculation(calculationOptions);
ViewBag.Started = true;
return View();
}
to
public ViewResult StartSlowCalculation(CalculationOptions calculationOptions)
{
Task.Run(() =>
{
WcfServiceProxy.DoSlowCalculation(calculationOptions);
});
ViewBag.Started = true;
return View();
}
which, as I understand should start an asynchronous request, and return immediately. Still, the execution is completely synchronous, and the UI is frozen until the operation concludes.
What obvious thing am I missing?
Update:
Also, note that I would prefer not to change the server implementation to an async one, just to de-synchronize the call to the service on the call-site.
Moreover, I've noticed that the StartSlowCalculation method finishes executing, but the server does not return a response until the service method finishes executing.
The WCF Service Proxy just does:
public void DoSlowCalculation(CalculationOptions calculationOptions)
{
//some logging code
Channel.DoSlowCalculation(calculationOptions);
}
so it's completely synchronous, however that shouldn't matter as it should be executed on an independent thread.
A task operation can run in the calling thread, it depends on taskScheduler decision. To help TaskScheduler make a right decision regarding long running call you can specify task creation option TaskCreationOptions.LongRunning.
And you can check whether task operation is running in a separate thread:
int launchedByThreadId = Thread.CurrentThread.ManagedThreadId;
int launchedInThreadId = -1;
Task.Run(() =>
{
launchedInThreadId = Thread.CurrentThread.ManagedThreadId;
WcfServiceProxy.DoSlowCalculation(calculationOptions);
});
// then compare whether thread ids are different
BTW, are you using any kind of Task.Wait() operation? It will block calling thread as well.
EDIT:
You might find following post interesting Is Task.Factory.StartNew() guaranteed to use another thread than the calling thread?
So try out using Task.Factory.StartNew() and specify cancellation token even you do not need it, sounds weird but it seems this guarantees that task will not be run eventually in the calling thread. Correct me If I wrong.
I've done this before.
The most robust way would be to use Asynchronous Controller's, or better yet an independant service such as a WCF service.
But in my experience, i've just needed to do "simple", one-liner task, such as auditing or reporting.
In that example, the easy way - fire off a Task:
public ViewResult StartSlowCalculation(CalculationOptions calculationOptions)
{
//Some Synchronous code.
Task.Factory.StartNew(() =>
{
WcfServiceProxy.DoSlowCalculation(calculationOptions);
});
ViewBag.Started = true;
return View();
}
That's a simple example. You can fire off as many tasks as you want, synchronize them, get notified when they finish, etc.
For more details you can see this links.
https://msdn.microsoft.com/en-us/library/dd321439(v=vs.110).aspx

Categories