Как избавиться от объектов, имеющих асинхронные методы? - PullRequest
14 голосов
/ 10 июня 2009

У меня есть этот объект PreloadClient, который реализует IDisposable, я хочу избавиться от него, но после того, как асинхронные методы завершат свой вызов ... чего не происходит

    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)
    }

Итак, какие-либо идеи или обходные пути ??

Ответы [ 7 ]

11 голосов
/ 10 июня 2009
  1. Вы не должны использовать конструкцию using, а располагать свои объекты, когда они больше не нужны:

    // 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. в этом случае вы должны распоряжаться всеми клиентами, выбрасывая основной класс:

    protected override Dispose(bool disposing)
    {
        foreach (PreloadClient client in _activeClients)
        { 
            client.PreloadCompleted -= client_PreloadCompleted;
            client.Dispose();
        }
        _activeClients.Clear();
        base.Dispose(disposing);
    }
    
  3. Обратите внимание, что эта реализация не является поточно-ориентированной

    • Доступ к списку _activeClients должен быть безопасным для потоков , так как ваш метод PreloadCompleted вызывается из другого потока
    • Содержащий объект может быть уничтожен до того, как клиент запустит событие. В этом случае «делать вещи» ничего не должно делать, так что это еще одна вещь, о которой вы должны позаботиться.
    • Возможно, было бы неплохо использовать блок try / finally внутри вашего обработчика событий, чтобы убедиться, что объект удаляется во всех случаях
3 голосов
/ 10 июня 2009

Почему бы не избавиться от клиента в обратном вызове?

1 голос
/ 10 июня 2009

У меня есть несколько идей:

  1. измени свою архитектуру.
  2. избавиться в обработчике
  3. использовать EventWaitHandle
0 голосов
/ 10 июня 2009

Почему бы не утилизировать в методе client_PreloadCompleted? Подобно тому, что предлагал thecoop, просто с помощью вызова Dispose внутри вышеуказанного метода, после того как вы получили доступ ко всем необходимым данным внутри объекта client.

Редактировать: Я думаю, это то, что предлагал и ориалмог.

0 голосов
/ 10 июня 2009

Асинхронное ожидание и детерминированное удаление не очень хорошо сочетаются. Если вы сможете найти способ разделения кода таким образом, чтобы одноразовые вещи помещались в один класс, а события - в другой, это упростило бы все.

0 голосов
/ 10 июня 2009

Ну, удаление объекта используется для уничтожения ресурсов, которые вы не хотите удерживать, пока GC (в конце концов) не придет и не соберет ваш объект. Ваш метод удаления уничтожает все, что вам нужно в client_PreloadCompleted?

Вы можете сделать так, чтобы объект располагался сам, когда произошли все ожидаемые обратные вызовы: сохраняйте «счетчик ссылок» для каждого ожидаемого обратного вызова и уменьшайте его для каждого происходящего обратного вызова - проверяйте нулевое значение в конце обработчика обратного вызова и утилизируйте, если поэтому.

Другой обходной путь: не беспокойтесь о IDisposable. GC заберет ваш объект. Вы, вероятно, не хотите, чтобы обработчик обратного вызова (который может быть не запущен) имел критическое состояние. Он (обратный вызов) должен просто открыть любые ресурсы, которые ему нужны, когда он вызывается, и затем закрыть их.

0 голосов
/ 10 июня 2009

Если регистрируются обработчики событий, вы не можете реально утилизировать объект, пока есть события, которые могут быть вызваны для него. Лучше всего сделать содержащийся класс доступным и сохранить клиента в переменной класса, которая будет удалена, когда содержащий класс равен.

Что-то вроде

class ContainingClass : IDisposable
{
    private PreloadClient m_Client;

    private void Preload(SlideHandler slide)
    {
         m_Client = new PreloadClient())

         m_Client.PreloadCompleted += client_PreloadCompleted;
         m_Client.Preload(slide);

    }
    private void client_PreloadCompleted(object sender, SlidePreloadCompletedEventArgs e)
    {
    }

    public void Dispose()
    {
        if (m_Client != null)
            m_Client.Dispose();
    }
}
...