How to dispose objects having asynchronous methods called?

Jalal El-Shaer picture Jalal El-Shaer · Jun 10, 2009 · Viewed 7.2k times · Source

I have this object PreloadClient which implements IDisposable, I want to dispose it, but after the asynchronous methods finish their call... which is not happening

    private void Preload(SlideHandler slide)
    {
        using(PreloadClient client = new PreloadClient())
        {                 
             client.PreloadCompleted += client_PreloadCompleted;
             client.Preload(slide);
        }
        // Here client is disposed immediately
    }
    private void client_PreloadCompleted(object sender, SlidePreloadCompletedEventArgs e)
    {
     // this is method is called after a while, 
     // but errors are thrown when trying to access object state (fields, properties)
    }

So, any ideas or work arounds ??

Answer

Groo picture Groo · Jun 10, 2009
  1. You shouldn't use the using construct, but rather dispose your objects when they are no longer needed:

    // keep a list of strong references to avoid garbage collection,
    // and dispose them all in case we're disposing the encapsulating object
    private readonly List<PreloadClient> _activeClients = new List<PreloadClient>();
    private void Preload(SlideHandler slide)
    {
        PreloadClient client = new PreloadClient();
        _activeClients.Add(client);
        client.PreloadCompleted += client_PreloadCompleted;
        client.Preload(slide);
    }
    
    private void client_PreloadCompleted(object sender,
         SlidePreloadCompletedEventArgs e)
    {
        PreloadClient client = sender as PreloadClient;
    
        // do stuff
    
        client.PreloadCompleted -= client_PreloadCompleted;
        client.Dispose();
        _activeClients.Remove(client);
    }
    
  2. in this case, you have to dispose all clients when disposing the main class:

    protected override Dispose(bool disposing)
    {
        foreach (PreloadClient client in _activeClients)
        { 
            client.PreloadCompleted -= client_PreloadCompleted;
            client.Dispose();
        }
        _activeClients.Clear();
        base.Dispose(disposing);
    }
    
  3. Note that this implementation is not thread safe

    • Access to the _activeClients list must be made thread-safe, as your PreloadCompleted method is called from a different thread
    • Your containing object may be disposed before a client fires the event. In that case "do stuff" should do nothing, so this is another thing you should take care of.
    • It might be a good idea to use a try/finally block inside your event handler, to make sure that the object gets disposed in all cases