Search code examples
c#asynchronousconcurrencydisposeiasyncdisposable

Perform time consuming disposal asynchronously


Imagine having a class that does some stuff and displays the results in modal window. The class has a costly dispose, i.e. it has to free resources which may take some seconds. The problem: When I close the window, I call the dispose method to free all the objects. This results in the window freezing for some seconds before closing. I want to avoid that.

Research brought me to IAsyncDisposable. My working implementation looks like this:

    public class MyClass : IAsyncDisposable, IDisposable
    {
        private bool disposedValue;

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // Disposing some costly stuff
                }
                disposedValue = true;
            }
        }

        public void Dispose()
        {
            Dispose(disposing: true);
            GC.SuppressFinalize(this);
        }

        public async ValueTask DisposeAsync()
        {
            await Task.Run(Dispose);
        }
    }

Calling it via:

myClassInstance.DisposeAsync().ConfigureAwait(false);

Question: Is this valid, especially the implementation of the DisposeAsync method? Can something go wrong here? It works nicely, but I am somehow not convinced.

I did some research on this but did not find anybody that did it this way. Even more, Visual Studio tells me that I should do something with the ValueTask I get from calling the DisposeAsync method. Any comments on that?


Solution

  • If you want to dispose something in the background (and that disposal is inherently synchronous), just dispose it in the background; for example instead of:

    obj.Dispose();
    

    consider:

    ThreadPool.QueueUserWorkItem<IDisposable>(static s => s.Dispose(), obj, false);
    

    Ultimately asynchronous and concurrent are related but different concepts; in this case what we want is concurrency, and async is unrelated - no need (or purpose) to add or remove it.