3

I'm aware that the best practice is to avoid async void methods for anything but async event handlers, and there is quite strong expert opinion against other use cases. I was however just involved in a brief discussion about usefulness of async void methods and I've got a couple of questions:

  • How does the Framework keep track of pending async void methods, including event handlers? Is there possibly a way to obtain the current list of them or cancel them (EDITED: tracking is probably possible by installing a custom SynchronizationContext)?
  • Are they any useful for fire-and-forget logging scenarious? I think they actually may be, as long as the correct time-stamp is preserved at the beginning of the method, while it still executes synchronously.
Community
  • 1
  • 1
noseratio
  • 59,932
  • 34
  • 208
  • 486

3 Answers3

3

How does the Framework keep track of pending async void methods, including event handlers?

The framework doesn't do anything special to keep track of async void methods. They're just like any other async method.

Also, your method either has a proper signature or it doesn't; event handlers do not care and have no logic to detect or work with async specifically.

A custom scheduler would be able to keep track of running tasks, but not have any specific knowledge if one is from an async void method or not. I don't think this is the right solution anyway -- if you find yourself needing to keep track of an async void method, you need to rethink your design.

Are they any useful for fire-and-forget logging scenarios? I think they actually may be, as long as the correct time-stamp is preserved

I don't know what you mean by a timestamp?

Async void is fine for any method where the caller will either never need to care about the result of the method call, or where it is being notified of the result elsewhere. These cases should be very exceedingly rare.

Fire-and-forget might be one such scenario, though I feel people often misuse fire-and-forget and end up just hiding important bugs from themselves.

Cory Nelson
  • 29,236
  • 5
  • 72
  • 110
  • By timestamp in the logging context I just meant the current time at the moment when the method was invoked, which should go to the log. – noseratio Nov 03 '13 at 21:33
  • Like any other async method, all code up to your first "await" will be executed synchronously. So, they would work for that purpose. I'd advise not to use fire-and-forget for anything other than trace logging, though. – Cory Nelson Nov 03 '13 at 21:37
  • I agree. Otherwise, a process may end up having a queue of unsaved log entries right before it crashes, especially if it's logging to a remote server. – noseratio Nov 03 '13 at 21:41
1

Regarding logging scenarios, here are two scenarios, with async-void being suitable for the first but less suitable for the second.

1) Logging the outcome of a long-running operation:

public static async void LogCompletion(Task operation, string title)
{
    try
    {
        await operation.ConfigureAwait(false);
        Log.Info($"{title} completed succesfully");
    }
    catch (Exception ex)
    {
        Log.Error($"{title} failed", ex);
    }
}

This usage resembles an async event handler, since the completion of an asynchronous operation is conceptually similar to the raising of an event. So this method essentially "handles" the completion "event" of a specific task. Converting this async-void method to an async Task LogCompletionAsync method wouldn't bring many benefits. It is true that an exception inside the LogCompletion method will crash the process, but the only possibility for an exception to occur is if the Log.Error throws. But if your logging framework starts throwing exceptions, your application is not going to stay alive for long anyway. And the sooner you learn about it the better, to start searching ASAP for a better logging framework.

2) Logging per se:

public static async void Log(string message)
{
    try
    {
        await File.AppendAllTextAsync(GetLogFilePath(),
            DateTime.Now.ToString() + " " +  message + "\r\n");
    }
    catch { }
}

This usage resembles calling an asynchronous method in a fire-and-forget fashion. Although it's not a terrible usage of async-void, it is a quite primitive and unsophisticated way of implementing logging in general. And it is quite inadvisable to try implementing it in the first place, since there are many high-quality implementations out there, available for free.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
0

Are they any useful for fire-and-forget logging scenarious?

Conceptually, I would say so, but if a task has an exception and it isn't being handled by waiting on it or accessing its Exception property which doesn't happen in an async void method it will tear down your application. So I would avoid that.

i3arnon
  • 113,022
  • 33
  • 324
  • 344
  • 3
    Since .NET 4.5, the default functionality ignores any unhandled exception, see [here](http://blogs.msdn.com/b/pfxteam/archive/2011/09/28/10217876.aspx). You are 100% correct in wanting to avoid that though, with a fire-and-forget scenario, it means you do not care about what happens, even if that thing went wrong, this could be a part of the justification of why the default behavior has been changed. – Lukazoid Nov 17 '13 at 00:37