Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fire-and-forget with async vs "old async delegate"

I am trying to replace my old fire-and-forget calls with a new syntax, hoping for more simplicity and it seems to be eluding me. Here's an example

class Program {     static void DoIt(string entry)      {          Console.WriteLine("Message: " + entry);     }      static async void DoIt2(string entry)     {         await Task.Yield();         Console.WriteLine("Message2: " + entry);     }      static void Main(string[] args)     {         // old way         Action<string> async = DoIt;         async.BeginInvoke("Test", ar => { async.EndInvoke(ar); ar.AsyncWaitHandle.Close(); }, null);         Console.WriteLine("old-way main thread invoker finished");         // new way         DoIt2("Test2");            Console.WriteLine("new-way main thread invoker finished");         Console.ReadLine();     } } 

Both approaches do the same thing, however what I seem to have gained (no need to EndInvoke and close handle, which is imho still a bit debatable) I am losing in the new way by having to await a Task.Yield(), which actually poses a new problem of having to rewrite all existing async F&F methods just to add that one-liner. Are there some invisible gains in terms of performance/cleanup?

How would I go about applying async if I can't modify the background method? Seems to me that there is no direct way, I would have to create a wrapper async method that would await Task.Run()?

Edit: I now see I might be missing a real questions. The question is: Given a synchronous method A(), how can I call it asynchronously using async/await in a fire-and-forget manner without getting a solution that is more complicated than the "old way"

like image 248
mmix Avatar asked Oct 09 '12 15:10

mmix


People also ask

Is asynchronous fire and forget?

Fire-and-Forget is most effective with asynchronous communication channels, which do not require the Originator to wait until the message is delivered to the Recipient. Instead, the Originator can pursue other tasks as soon as the messaging system has accepted the message.

Does async-await improve performance?

C# Language Async-Await Async/await will only improve performance if it allows the machine to do additional work.

What is the difference between async void and async task?

A Task returning async method can be awaited, and when the task completes, the continuation of the task is scheduled to run. A void returning async method cannot be awaited; it is a "fire and forget" method. It does work asynchronously, and you have no way of telling when it is done.

Is async always better?

Asynchronous programming is a better fit for code that must respond to events – for example, any kind of graphical UI. An example of a situation where programmers use async but shouldn't is any code that can focus entirely on data processing and can accept a “stop-the-world” block while waiting for data to download.


2 Answers

Avoid async void. It has tricky semantics around error handling; I know some people call it "fire and forget" but I usually use the phrase "fire and crash".

The question is: Given a synchronous method A(), how can I call it asynchronously using async/await in a fire-and-forget manner without getting a solution that is more complicated than the "old way"

You don't need async / await. Just call it like this:

Task.Run(A); 
like image 138
Stephen Cleary Avatar answered Oct 01 '22 04:10

Stephen Cleary


As noted in the other answers, and by this excellent blog post you want to avoid using async void outside of UI event handlers. If you want a safe "fire and forget" async method, consider using this pattern (credit to @ReedCopsey; this method is one he gave to me in a chat conversation):

  1. Create an extension method for Task. It runs the passed Task and catches/logs any exceptions:

    static async void FireAndForget(this Task task) {    try    {         await task;    }    catch (Exception e)    {        // log errors    } } 
  2. Always use Task style async methods when creating them, never async void.

  3. Invoke those methods this way:

    MyTaskAsyncMethod().FireAndForget(); 

You don't need to await it (nor will it generate the await warning). It will also handle any errors correctly, and as this is the only place you ever put async void, you don't have to remember to put try/catch blocks everywhere.

This also gives you the option of not using the async method as a "fire and forget" method if you actually want to await it normally.

like image 37
BradleyDotNET Avatar answered Oct 01 '22 03:10

BradleyDotNET