I have an application that regularly launches fire-and-forget tasks, mainly for logging purposes, and my problem is that when the application is closed, any currently running fire-and-forget tasks are aborted. I want to prevent this from happening, so I am searching for a mechanism that will allow me to await
the completion of all running fire-and-forget operations before closing my app. I don't want to handle their possible exceptions, I don't care about these. I just want to give them the chance to complete (probably with a timeout, but this is not part of the question).
You could argue that this requirement makes my tasks not truly fire-and-forget, and there is some truth in that, so I would like to clarify this point:
- The tasks are fire-and-forget locally, because the method that launches them is not interested about their outcome.
- The tasks are not fire-and-forget globally, because the application as a whole cares about them.
Here is a minimal demonstration of the problem:
static class Program
{
static async Task Main(string[] args)
{
_ = Log("Starting"); // fire and forget
await Task.Delay(1000); // Simulate the main asynchronous workload
CleanUp();
_ = Log("Finished"); // fire and forget
// Here any pending fire and forget operations should be awaited somehow
}
private static void CleanUp()
{
_ = Log("CleanUp started"); // fire and forget
Thread.Sleep(200); // Simulate some synchronous operation
_ = Log("CleanUp completed"); // fire and forget
}
private static async Task Log(string message)
{
await Task.Delay(100); // Simulate an async I/O operation required for logging
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} {message}");
}
}
Output:
11:14:11.441 Starting
11:14:12.484 CleanUp started
Press any key to continue . . .
The "CleanUp completed"
and "Finished"
entries are not logged, because the application terminates prematurely, and the pending tasks are aborted. Is there any way that I can await them to complete before closing?
Btw this question is inspired by a recent question by @SHAFEESPS, that was sadly closed as unclear.
Clarification: The minimal example presented above contains a single type of fire-and-forget operation, the Task Log
method. The fire-and-forget operations launched by the real world application are multiple and heterogeneous. Some even return generic tasks like Task<string>
or Task<int>
.
It is also possible that a fire-and-forget task may fire secondary fire-and-forget tasks, and these should by allowed to start and be awaited too.