Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Disposal of AsyncLazy, what is the right (easy to use and non-leaky) way?

Tags:

c#

async-await

I'm using a specialization of Stephen Cleary's AsyncLazy implementation, from his blog.

/// <summary>
/// Provides support for asynchronous lazy initialization.
/// This type is fully thread-safe.
/// </summary>
/// <typeparam name="T">
/// The type of object that is being asynchronously initialized.
/// </typeparam>
public sealed class AsyncLazy<T>
{
    /// <summary>
    /// The underlying lazy task.
    /// </summary>
    private readonly Lazy<Task<T>> instance;

    /// <summary>
    /// Initializes a new instance of the 
    /// <see cref="AsyncLazy&lt;T&gt;"/> class.
    /// </summary>
    /// <param name="factory">
    /// The delegate that is invoked on a background thread to produce
    /// the value when it is needed.
    /// </param>
    /// <param name="start">
    /// If <c>true</c> commence initialization immediately.
    /// </param>
    public AsyncLazy(Func<T> factory, bool start = false)
    {
        this.instance = new Lazy<Task<T>>(() => Task.Run(factory));
        if (start)
        {
            this.Start();
        }
    }

    /// <summary>
    /// Initializes a new instance of the 
    /// <see cref="AsyncLazy&lt;T&gt;"/> class.
    /// </summary>
    /// <param name="factory">
    /// The asynchronous delegate that is invoked on a background 
    /// thread to produce the value when it is needed.
    /// </param>
    /// <param name="start">
    /// If <c>true</c> commence initialization immediately.
    /// </param>
    public AsyncLazy(Func<Task<T>> factory, bool start = false)
    {
        this.instance = new Lazy<Task<T>>(() => Task.Run(factory));
        if (start)
        {
            this.Start();
        }
    }

    /// <summary>
    /// Asynchronous infrastructure support.
    /// This method permits instances of
    /// <see cref="AsyncLazy&lt;T&gt;"/> to be await'ed.
    /// </summary>
    public TaskAwaiter<T> GetAwaiter()
    {
        return this.instance.Value.GetAwaiter();
    }

    /// <summary>
    ///     Starts the asynchronous initialization, 
    ///     if it has not already started.
    /// </summary>
    public void Start()
    {
        var unused = this.instance.Value;
    }
}

This is great code and I really appreciate how easy it is to use. i.e.

class SomeClass
{
    private readonly AsyncLazy<Thing> theThing = new AsyncLazy<Thing>(
        () => new Thing());

    void SomeMethod()
    {
        var thing = await theThing;
        // ...
    }
}

Now my question,

Suppose that SomeClass inherits from a class that implements IDisposable and that Thing implements IDisposable. We'd have skeleton implementation like this,

class SomeClass : SomeDisposableBase
{
    private readonly AsyncLazy<Thing> theThing = new AsyncLazy<Thing>(
        () => new Thing());

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            // What do I do with theThing?
        }

        base.Dispose(disposing);
    }
}

So, what do I do with theThing in the Dispose override? Should I extend AsyncLazy<T> to have a new property?

// ...

public bool IsStarted
{
    get
    {
        return this.instance.IsValueCreated;
    }
}

// ...

Should I change AsyncLazy<T> to implement IDisposable?

Have I misunderstood and I don't need to worry?

Should I do something else?

like image 602
Jodrell Avatar asked Jul 04 '14 16:07

Jodrell


2 Answers

Stephen Toub's version of this class inherits from Lazy<Task<T>>, so you get the IsValueCreated property automatically.

Alternatively, you could expose the IsValueCreated property from the private field:

public sealed class AsyncLazy<T>
{
    private readonly Lazy<Task<T>> instance;
    ...
    public bool IsValueCreated
    {
        get { return instance.IsValueCreated; }
    }
}

For consistency with the built-in Lazy<T> type, I'd avoid renaming the property to IsStarted.

like image 166
Richard Deeming Avatar answered Sep 22 '22 01:09

Richard Deeming


You can use a bool inside the AsyncLazy<T> initialization to know if theThing has been initialized

class SomeClass : SomeDisposableBase
{
   public SomeClass()
   {
      theThing = new AsyncLazy<Thing>(() => 
      { 
         _isInitialized = true;
         return new Thing();
      } 
   }

   private bool _isInitialized;
   private readonly AsyncLazy<Thing> theThing;

protected override void Dispose(bool disposing)
{
    if (disposing && _isInitialized)
    {
        // Dispose Thing
    }

    base.Dispose(disposing);
 }
}

Although, if this pattern occurs in your code more than once, then i would definitely extend AsyncLazy

like image 37
Yuval Itzchakov Avatar answered Sep 20 '22 01:09

Yuval Itzchakov