General answers, such as here and here , so as not to include or forget questions, do not use async / await, but instead use Task.Run or TaskFactory.StartNew , passing in a synchronous method.
However, sometimes the method I want to run and forget is asynchronous, and there is no equivalent synchronization method.
Update note / warning: As Stephen Cleary has shown, it is dangerous to continue working on a request after you send a response. The reason is that AppDomain may be closed while this work is still ongoing. For more information, see Link in his answer. In any case, I just wanted to point this out in advance so that I would not send anyone along the wrong path.
I think my case is valid because the actual work is done by another system (another computer on another server), so I only need to know that the message remains for this system. If there is an exception, the server or the user cannot do anything, and this does not affect the user, all I need to do is turn to the exception log and clear it manually (or implement some kind of automated mechanism). If AppDomain is turned off, I will have a residual file on the remote system, but I will choose this as part of my usual maintenance cycle, and since its existence is no longer known to my web server (database) and its name is uniquely timestamped, it will not cause any problems while he still lingers.
It would be ideal if I had access to the persistence mechanism, as Stephen Cleary pointed out, but, unfortunately, I was not at that time.
I thought I was just pretending that the DeleteFoo request completed normally on the client side (javascript), leaving the request open, but I need the information in the response to continue, so it will keep things up.
So the original question ...
eg:
In my asp.net mvc code, I want to call DeleteFooAsync in fire-and-forget mode - I don't want to delay a response waiting for DeleteFooAsync to complete. If for some reason DeleteFooAsync fails (or throws an exception), the user or the program cannot do anything, so I just want to register an error.
Now I know that any exceptions will lead to inconspicuous exceptions, so the simplest case that 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 involved?
Another option I can think of is to create my own shell, for example:
private void async DeleteFooWrapperAsync() { try { await DeleteFooAsync(); } catch(Exception exception ) { m_log.Error("DeleteFooAsync failed: " + exception.ToString()); } }
and then call it using TaskFactory.StartNew (possibly completing the async action). However, this looks like a lot of shell code every time I want to call the async method in fire and forget mode.
My question is, is this the correct way to call the asynchronous method in fire-and-forget mode?
UPDATE:
Well, I found that the following is in my controller (not that the controller action should be asynchronous, because other pending asynchronous calls are expected):
[AcceptVerbs( HttpVerbs.Post )] public async Task<JsonResult> DeleteItemAsync() { Task deleteTask = DeleteFooAsync(); ... }
caused a form exception:
Unhandled exception: System.NullReferenceException: The object reference is not set to the object instance. in System.Web.ThreadContext.AssociateWithCurrentThread (BooleansetImpersonationContext)
It is discussed here and seems to be related to the SynchronizationContext, and "the returned task was transferred to the terminal state before all async work was completed."
So the only way that worked was:
Task foo = Task.Run( () => DeleteFooAsync() );
My understanding of why this works is that StartNew gets a new thread for DeleteFooAsync to work.
Unfortunately, Scott's suggestion below does not work to handle exceptions in this case, since foo is no longer the DeleteFooAsync task, but rather the Task.Run task, and therefore does not handle exceptions from DeleteFooAsync. My UnobservedTaskException is ultimately thrown, so at least it still works.
So, I guess the question still stands, how do you shoot and forget the async method in asp.net mvc?