Justin Lessard's answer explains the difference between using
and await using
, so I'll focus on which one to use. There are two cases: either the two methods Dispose
/DisposeAsync
are complementary, or they are doing something different.
If the methods are complementary, which is the common case, you can call either one of them and the result will be the same: the unmanaged resources will be released. There is no reason to call both of them, sequentially. If you do, the second call will be a no-op: the resources are already released, so there will be nothing more to do. Choosing which one to call is easy: if you are in a synchronous context, call the Dispose()
(use the using
). If you are in an asynchronous context, call the await DisposeAsync()
(use the await using
)¹.
If the methods are doing something different, you should read the documentation and decide which behavior is more suitable for the scenario at hand. Let's talk for example for the System.Threading.Timer
class, that implements both interfaces (IDisposable
and IAsyncDisposable
). The Dispose
method releases the unmanaged resources as expected, but the DisposeAsync
is doing something more than that: it also awaits the completion of any callbacks that are currently running. Let's make an experiment to demonstrate this difference:
var stopwatch = Stopwatch.StartNew();
using (new Timer(_ => Thread.Sleep(1000), null, 0, Timeout.Infinite))
{
Thread.Sleep(100);
}
Console.WriteLine($"Duration: {stopwatch.ElapsedMilliseconds:#,0} msec");
We create a timer that fires after 0 msec, so practically immediately, then we wait for 100 msec to be sure that the callback has been invoked (it is invoked on a ThreadPool
thread), and then we dispose the timer synchronously. Here is the output of this experiment:
Duration: 102 msec
Now let's switch from using
to await using
. Here is the output of the second experiment:
Duration: 1,005 msec
The implicit call to DisposeAsync
returned a ValueTask
that completed only after the completion of the timer's callback.
So in the case of a Timer
, choosing between using
and await using
is not just a choice that depends on the context. You might prefer to use the synchronous using
in an async context, because you don't care about the callback (you know that it's not harmful to let it become fire-and-forget). Or you might be in a synchronous context but you might prefer the behavior of await using
, because fire-and-forget is unacceptable. In that case you'll have to abandon the convenience of using
, and invoke instead the DisposeAsync
explicitly in a finally
block:
var timer = new Timer(_ => Thread.Sleep(1000), null, 0, Timeout.Infinite);
try { Thread.Sleep(100); }
finally { timer.DisposeAsync().AsTask().Wait(); }
¹ Be aware, especially if you are writing a library, that the await using
captures the synchronization context by default. In case this is undesirable, which usually is for library code, you'll have to configure it with ConfigureAwait(false)
. This has some implications that are discussed here: How do I get the "await using" syntax correct?