2

I'm developing an application for monitoring certain tasks (e.g. if certain services/websites are currently up and running, certain records in the database exist, etc.). And as most these tasks are long running, I use TPL with async/await.

I have an base class for all such tasks:

public abstract class LongRunningOperation
{
    // .. some props...

    internal async void Start()
    {
        try
        {
            this.Status = OperationStatus.Started;
            await this.DoStart();
            this.Status = OperationStatus.Finished;
        }
        catch (Exception e)
        {
            this.Status = OperationStatus.Error;
            this.Message = e.ToString();
        }
    }

    protected abstract Task DoStart();
}

And method that launches these tasks looks like this:

public static LongRunningOperation[] LaunchOperations()
{
    LongRunningOperation[] operations = GetAllLongRunningOperations();
    foreach (var o in operations)
        Task.Factory.StartNew(() => { o.Start(); });
    return operations;
}

The array returned by this method is used to monitor all LongRunningOperations and log the stats. currently I have a console application having a while (true) loop that prints out the stats (name, status, current runtime) for each operation on the screen (refreshing every second) until all the operations are finished.

The thing that bothers me is the async void method. I've read that it's bad practice to use async void methods, but:

  • I can't figure out what harm they might do in my scenario
  • If I change the Start method to return Task, its return value will never be used anywhere, and I can't think why I need it

I'd appreciate it if someone could clarify these points

ranieuwe
  • 2,268
  • 1
  • 24
  • 30
Andre Borges
  • 1,360
  • 14
  • 37
  • 1
    Don't know if duplicate, but could be: http://stackoverflow.com/questions/12144077/async-await-when-to-return-a-task-vs-void – J. Steen Aug 17 '16 at 11:53
  • 2
    The only place where it makes sense to use `async void` is when you need to deal with event handlers on a synchronization context - the synchronization contexts makes it safe, and event handlers must not return a value. Use `async Task` anywhere else - there's no reason not to. Your code already looks a bit suspicious - e.g. using `Task.Factory.StartNew` instead of `Task.Run` and using it to start an asynchronous method. Why not use the task `Start` returns to track the status of the long-running operation instead of your own (likely thread-unsafe) flags? – Luaan Aug 17 '16 at 12:30
  • @Luaan, I'm not returning `Task` because I don't need the `Task` object in the calling code (I'm tracking operation state usnig other means). And if I change it to return `Task` and keep the call as it is `o.Start()`, I get a warning suggesting to add `await` to the call, which is something I don't need at all. – Andre Borges Aug 17 '16 at 13:51
  • In any case, what I want to know is what _specific_ problems this code can cause – Andre Borges Aug 17 '16 at 13:55
  • It's interesting that you care about the warning, but not about the *message* in the warning - which applies exactly the same for your `async void` case. It's just that the compiler has no way of knowing if the method is asynchronous in that case, because `async` isn't part of the signature. You're suppressing the warning by having `async void`, just like if you used `#pragma` to suppress it later - but worse, since it's less visible. It doesn't cause any *specific* problems - it's just a bad practice that prevents the caller from tracking the asynchronous operation. – Luaan Aug 17 '16 at 15:23

1 Answers1

7

An async void method is a "fire and forget" operation. You can not wait for any result, and will not know when the operation completes and if it has been successful or not.

Basically you should use void when you are sure that you'll never need to know when the operation finished and if the operation execution was successful or not (for example writing logs).

With async methods that return Task, a caller is capable of waiting for an operation to finish, and also handle exceptions that happened during the execution of the operation.

To summarize, if you do not need a result, an async Task is slightly better because you can await it as well as handle exceptions and deal with task ordering.

ranieuwe
  • 2,268
  • 1
  • 24
  • 30
  • 1
    Even for writing logs kind of operation you shall never use void return for an async method, it is their only for events, which are special case – Mrinal Kamboj Aug 17 '16 at 12:32
  • 1
    @MrinalKamboj, "Even for writing logs kind of operation you shall never use void return" - why not? – Andre Borges Aug 17 '16 at 13:57
  • @Andre Borges Please check the link provided by J Steen above, that shall answer your question in detail. Even if you want to fire and forget, what you don't want to do is, not having any control on the failure of the that call and thus impacting your current call – Mrinal Kamboj Aug 17 '16 at 16:14