Use cases for async void methods, revisited - c#

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.

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.

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.

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.

Related

From sync to async scenario

I have this event handler:
private void OnShutdownRequested(object? sender, ShutdownRequestedEventArgs e)
{
var canShutdown = lifetime.CanShutdown();
e.Cancel = !canShutdown;
}
Now, due to design decisions the CanShutdown method has changed from bool to Task<bool>
Task<bool> CanShutDown()
{
//...
}
So, I need to modify the event handler like this:
private async void OnShutdownRequested(object? sender, ShutdownRequestedEventArgs e)
{
var canShutdown = await lifetime.CanShutdown();
e.Cancel = !canShutdown;
}
I've read many times that async void methods have many problems, like unhandled exceptions being thrown directly inside the SynchronizationContext. But one of the valid usages for them are event handlers. This is an event handler. Isn't it?
BUT, I wonder if that code is free of undesired consequences after the "migration".
My concern is that the handler modifies the value of e.Cancel.
A colleague has told me that this will happen:
After await, the caller to that method isn't awaiting. It assumes synchronous execution, so it immediately reads e.Cancel, and that hasn't been set yet.
That is a problem inside event handler: You realize as soon as the await keyword is hit, the code that called ShutdownRequested.Invoke() immediately returns. The value it will read might not be up-to-date.
I'm afraid my colleague has his point. So, it seems this approach is broken. But I still don't see how to fix that.
How to deal with the EventArgs being shared by sync and async code?
I've read many times that async void methods have many problems, like unhandled exceptions being thrown directly inside the SynchronizationContext. But one of the valid usages for them are event handlers.
Yes. In fact, throwing exceptions on the SynchronizationContext is actually deliberate behavior specifically to emulate the behavior of event handlers.
One of the primary problems with async void method is that it's not easy to determine when the async void method has completed. Which is exactly the problem your colleague is pointing out.
This is an event handler. Isn't it?
Sort of.
Take a step back and consider the design patterns being used. The Observer pattern is a way to notify observers of state changes. The Observer pattern is a clear fit for "events" in OOP: any number of observers may subscribe to state change notifications.
This kind of "shutdown" notification is not just a notification, though. It also has a return value. Generally, this is the Strategy pattern. The Strategy pattern is not a good fit for events in OOP. However, many times the Strategy pattern is (mis-)implemented with an event; this is a common design mistake in OOP languages.
So, is it an event handler? Technically, yes. Should it be an event handler? Probably not.
In the synchronous world, implementing the Strategy pattern with events is often "close enough". Sometimes there's some confusion about how the return value should be handled in case there are multiple event subscribers, but in general this kind of design mistake goes unnoticed. Until async comes along, and suddenly the design mistake of using events for the Strategy pattern becomes more apparent.
But I still don't see how to fix that.
I describe a few possibilities on my blog.
If you control OnShutdownRequested and ShutdownRequestedEventArgs, then you can use deferrals. It's a bit complex to set up, but it allows both synchronous and asynchronous handlers (as long as the handlers use a deferral), and the code that raises the event can (asynchronously) wait for all handlers to complete before retrieving the results.
If shutdown is the only event you have to worry about, then one common trick is to always set Cancel to true, do the asynchronous work, and then if the shutdown is permitted, explicitly do a shutdown at the end of that asynchronous work.
Your colleague is correct, the posted example code will most likely not work as intended.
I would suggest making ShutdownRequestedEventArgs contain something like a List<Task<bool>>, so the caller can await all the task to determine if any of them wants to cancel before shutting down. This might also include a timeout, so that some hanged task does not block the process forever. This moves the problem from the handler of the event to the caller, that hopefully is in a better position to deal with the problem.
Another possibility could be to wait for the task synchronously:
private void OnShutdownRequested(object? sender, ShutdownRequestedEventArgs e)
{
var canShutdown = lifetime.CanShutdown().Result;
e.Cancel = !canShutdown;
}
But this may be dangerous in a UI program. If the ShutdownRequested is requested on the UI thread, and CanShutdown needs to execute any code on the UI thread, then the program will deadlock. And this is fairly likely if CanShutdown is not written specifically to avoid this. See configureAwait for more details .
You might also just revert to the synchronous solution. An event like ShutdownRequested does not sound like it is a good fit for an asynchronous solution. I would have expected that such an event would require a immediate response. But I do not know the background of your application or why the method was changed in the first place, so there might very well be good reasons for it.

C# what is the risk of calling an async method and never awaiting the returned task or storing references to said Task

I know that exception handling can become strange if you never await a returned Task, but if you don't actually care about the results and/or the success of the async method, does it matter if you don't store a reference to the task anywhere? Basically I just want to fire and forget the method, and I want to know how pedantic I need to be when doing so.
My specific use case is an async dispose method, which waits for any outstanding tasks to finish running before it disposes any HTTPClients.
public async Task Dispose(){
try{
...
}catch{} //might add some minimal logging, but probably wont
}
In descending order of laziness, the implementations I've been considering are
public void DisposeFoo(){
foo.Dispose()//feed the task to the void
}
is the above any more or less safe than
private Task DisposeFooTask;
public void DisposeFoo(){
DisposeFooTask= foo.Dispose()//store the task and never touch it again
}
And I also found this approach on SO
static async void FireAndForget(this Task task)
{
try
{
await task;
}
catch (Exception e)
{
// log errors
}
}
public void DisposeFoo(){
foo.Dispose().FireAndForget();
}
Is there any risk of an error in foo.Dispose() escaping any try catches and killing my system if I ignore the Task? And if not, is there any risk of something like the thread running foo.Dispose() giving up on life due to the task not being in scope anymore? Or is the danger with my lazy coding purely exception hiding?
if you don't actually care about the results and/or the success of the async method, does it matter if you don't store a reference to the task anywhere?
A task will generally not be eligible for garbage collection until it completes; this is because there is some kind of callback (that will complete the task) that references that task, and those callbacks are generally rooted (in GC terms).
When a task is ignored (i.e., completed but not observed), then it can become eligible for garbage collection. If that task completes with an exception, then it will raise TaskScheduler.UnobservedTaskException when it is GC'ed. This event used to crash the process but no longer does.
If you want to avoid raising the event completely, then you can use the FireAndForget wrapper to explicitly observe and ignore the exception.
However, the primary problem with fire-and-forget tasks is that your code cannot know when the task completes. Which is the whole point of fire-and-forget tasks, but it's amazing how many people think they want "fire-and-forget" but then want to ensure that the tasks complete. This is a common problem when determining when it is safe to exit the process.
My specific use case is an async dispose method, which waits for any outstanding tasks to finish running before it disposes any HTTPClients.
Calling dispose as a fire-and-forget task would be OK. Shutdown isn't a consideration in this case, since the OS will clean up anyway.
However, calling dispose in a task at all is kind of odd. Disposal is usually extremely fast (like literally setting a field or two), so it doesn't make sense to push that work to a background thread.
Is there any risk of an error in foo.Dispose() escaping any try catches and killing my system if I ignore the Task?
There are uncatchable errors, but these would close the process even if you tried to handle the errors from the task. So in practice the task should always complete.
And if not, is there any risk of something like the thread running foo.Dispose() giving up on life due to the task not being in scope anymore?
No, the task would be referenced by a taskscheduler, and this would keep it alive until it can be started. And once it is running it is kept alive by the thread running it.
Or is the danger with my lazy coding purely exception hiding?
Yes, as far as I can tell the worst effect would be to hide potential exceptions.

C# Async when also needing support for synchronous call

I'm in a situation where we have some code that is run by user input (button click), that runs through a series of function calls and result in generating some data (which is a quite heavy operation, several minutes). We'd like to use Async for this so that it doesn't lock up the UI while we're doing this operation.
But at the same time we also have a requirement that the functions will also be available through an API which preferably should be synchronous.
Visualization/Example (pseudo-code):
public async void Button_Click() // from UI context
{
await instanceOfClassA.FuncA();
// some code
}
public async Task ClassA.FuncA()
{
await instanceOfClassB.FuncB()
// some code
}
public async Task ClassB.FuncB()
{
await instanceOfClassC.SomeHeavyFunc()
// some code
}
public async Task ClassC.SomeHeavyFunc()
{
// some heavy calculations
}
// Also need to provide a public synchronous API function
public void SomeClass.SynchronousAPIFunc()
{
// need to call differentInstanceOfClassB.FuncB()
}
Is there a way to make it so that the public API function does the waiting for the async operation internally?
EDIT:
In this post, user Rachel provides two answers to the question. Both seem interesting, though I'm unsure which one would offer the least amount of risk/side effects.
EDIT2:
I should note that we're using .NET v4.6.1.
Thanks in advance.
The problem with making "synchronous" versions of your methods that just call the asynchronous versions is that it can cause deadlocks, especially if the person calling this code is not aware that this is what is happening.
If you really want to make synchronous versions, then follow Microsoft's lead and write completely new methods that do not use any asynchronous code. For example, the implementation for File.ReadAllLines() doesn't use any of the same code as File.ReadAllLinesAsync().
If you don't want to do that, then just don't provide synchronous versions of your methods. Let the caller make the decision on how to deal with it. If they want to block synchronously on it, then they can mitigate the risk of deadlock.
But at the same time we also have a requirement that the functions will also be available through an API which preferably should be synchronous.
If you have the need to expose both a synchronous and asynchronous API, I recommend the boolean argument hack. This looks like:
public Task<T> FuncBAsync() => FuncBAsync(sync: false);
public T FuncB() => FuncBAsync(sync: true).GetAwaiter().GetResult();
public async Task<T> FuncBAsync(bool sync)
{
// Note: is `sync` is `true`, this method ***must*** return a completed task.
...
}
Is there a way to make it so that the public API function does the waiting for the async operation internally?
I do not recommend using direct blocking (e.g., GetAwaiter().GetResult()), as the straightforward implementation will lead to deadlocks.
EDIT: In this post, user Rachel provides two answers to the question.
I strongly recommend against using that solution. It uses a nested message loop with a custom SynchronizationContext, but doesn't do COM pumping. This can cause problems particularly if called from a UI thread. Even if the pumping isn't a problem, this solution can cause unexpected re-entrancy, which is a source of countless, extremely subtle, and difficult-to-find bugs.
You can utilize .GetAwaiter().GetResult()
as per your example, it would look like:
public void SomeClass.SynchronousAPIFunc()
{
// need to call differentInstanceOfClassB.FuncB()
ClassB.FuncB().GetAwaiter().GetResult();
}
Also, a good reference on when to not use the above can be found at Dont Block on Async Code

Why wrap event assignments in async anonymous functions?

Is there any point in wrapping event assignments in anonymous async functions?
Lets say this code is in some adapter class:
public event Action<int> someAction;
The following code is in some other class that sets the action:
Example 1:
_someAdapter.someAction += async (someParameter) =>
{
await HandleAction(someParamter);
};
In this case HandleAction has async Task return type.
Example 2:
_someAdapter.someAction += HandleAction;
In this case HandleAction has async void return type.
The same question applies to button click event handlers etc. I have seen examples of both and was wondering why this gets wrapped sometimes. From my testing there doesn't seem to be any difference.
I believe that using an anonymous handler wrapper you'll be able to let the C# Garbage Collector just "unhook" and destroy the handler on this (on example 1) when it is about to be destroyed.
Event handlers outside the this (it isn't the case in your example) would have special attention and wrap it looks a good choice.
If this will live a long time and there is some possibility of you have to hook that event again you should use another strategy to avoid memory leak like ones discussed on this question
The return types differences are related to exception handling and it's ok to use an async void on top-level methods and async event handlers.
I hope it helps.
In your example, there would be likely be no noticeable difference. This is more about coding standards and best practices. In general, it is not recommended that you create 'async void' functions, if you can avoid it. The purpose of that feature is to allow async code in functions that can't change their signature (like overrides). An 'async void' function has disadvantages like swallowing exceptions and being less composable.
If, for instance, you wanted to call HandleAction from another place/method (often this is the reason for using a named method instead of an anonymous function), you would not be able to await it unless it returns Task.
Also, if HandleAction threw an exception, you would not be able to catch it if you can't await it, so you better be sure it will handle all exceptions internally.
I won't recommend to use an anonymes delegate as an event handler. The reason is that it might come to cycling references and thus the garbage collector won't be able to free up memory - so called memory leak.
Especially in the Xamarin-world, where the managed garbage collector often won't know the real size of an object, developers should have an eye on memory and allocations their applications needs.
By avoiding anonymes delegates you are able to break the cycle simple by removing the event handler assignment.
I can't comment on your adapter handlers, but... seeing as you say "The same question applies to...", the answer might be the same as for button click event handlers, etc, which is (arguably the largest benefit of async in UI applications) - you get to perform expensive async operations in the handlers off the UI thread.
E.g.
_button.Click += async (...) =>
{
int answer = await ExpensiveHttpResultThatTakes10Seconds();
_answer.Text = answer.ToString();
};
If that wasn't async, the UI would freeze up for 10 seconds, not responding to clicks, keys, etc.
Because it is async, the UI remains responsive during those 10 seconds, as the await is non-blocking, processing UI events and even other event handlers.

How to better handle disposed controls when using async/await

Consider this code that runs on the UI thread:
dividends = await Database.GetDividends();
if (IsDisposed)
return;
//Do expensive UI work here
earnings = await Database.GetEarnings();
if (IsDisposed)
return;
//Do expensive UI work here
//etc...
Note that every time I await I also check IsDisposed. It's necessary because say I await on a long running Task. Meanwhile the user closes the form before it completes. The Task will finish and run a continuation that attempts to access controls on a disposed form. An exception occurs.
Is there a better way to handle this or simplify this pattern? I use await liberally in UI code and it's both ugly to check for IsDisposed every time and error prone if I forget.
EDIT:
There are a few proposed solutions that don't fit the bill because they change functionality.
Prevent form closing until background tasks complete
This will frustrate the users. And it also still allows potentially expensive GUI work to occur that is a waste of time, hurts performance and is no longer relevant. In the case where I'm almost always doing background work this could prevent the form close for a very long time.
Hide the form and close it once all tasks complete
This has all the problems of preventing the form close except doesn't frustrate users. The continuations that do expensive GUI work will still run. It also adds complexity of tracking when all tasks complete and then closing the form if it's hidden.
Use a CancellationTokenSource to cancel all tasks when the form is closing
This doesn't even address the problem. In fact, I already do this (no point in wasting background resources either). This isn't a solution because I still need to check IsDisposed due to an implicit race condition. The below code demonstrates the race condition.
public partial class NotMainForm : Form
{
private readonly CancellationTokenSource tokenSource = new CancellationTokenSource();
public NotMainForm()
{
InitializeComponent();
FormClosing += (sender, args) => tokenSource.Cancel();
Load += NotMainForm_Load;
Shown += (sender, args) => Close();
}
async void NotMainForm_Load(object sender, EventArgs e)
{
await DoStuff();
}
private async Task DoStuff()
{
try
{
await Task.Run(() => SimulateBackgroundWork(tokenSource.Token), tokenSource.Token);
}
catch (TaskCanceledException)
{
return;
}
catch (OperationCanceledException)
{
return;
}
if (IsDisposed)
throw new InvalidOperationException();
}
private void SimulateBackgroundWork(CancellationToken token)
{
Thread.Sleep(1);
token.ThrowIfCancellationRequested();
}
}
The race condition happens when the task has already completed, the form has closed, and the continuation still runs. You will see InvalidOperationException being thrown occasionally. Cancelling the task is good practice, sure, but it doesn't alleviate me from having to check IsDisposed.
CLARIFICATION
The original code example is exactly what I want in terms of functionality. It's just an ugly pattern and doing "await background work then update GUI" is a quite common use case. Technically speaking I just want the continuation to not run at all if the form is disposed. The example code does just that but not elegantly and is error prone (if I forget to check IsDisposed on every single await I'm introducing a bug). Ideally I want to write a wrapper, extension method, etc. that could encapsulate this basic design. But I can't think of a way to do this.
Also, I guess I must state performance is a first-class consideration. Throwing an exception, for example, is very expensive for reasons I won't get into. So I also don't want to just try catch ObjectDisposedException whenever I do an await. Even uglier code and also hurts performance. It seems like just doing an IsDisposed check every single time is the best solution but I wish there was a better way.
EDIT #2
Regarding performance - yes it is all relative. I understand the vast majority of developers don't care about the cost of throwing exceptions. The true cost of throwing an exception is off-subject. There is plenty of information available on this elsewhere. Suffice to say it's many orders of magnitude more expensive than the if (IsDisposed) check. For me, the cost of needlessly throwing exceptions is unacceptable. I say needless in this case because I already have a solution that doesn't throw exceptions. Again, letting a continuation throw an ObjectDisposedException is not an acceptable solution and exactly what I'm trying to avoid.
I also use IsDisposed to check the state of the control in such situations. Although it is a bit verbose, it is no more verbose than necessary to handle the situation - and it is not confusing at all. A functional language like F# with monads could probably help here - I'm no expert - but this seems as good as it gets in C#.
It should be pretty straightforward to have a CancellationTokenSource owned by your form, and have the form call Cancel when it is closed.
Then your async methods can observe the CancellationToken.
I once solved a similar issue by not closing the form. Instead, I hid it at first and only really closed it when all outstanding work had completed. I had to track that work, of course, in form of Task variables.
I find this to be a clean solution because disposal issues do not arise at all. Yet, the user can immediately close the form.

Categories