15

我有这个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)
    }

那么,有什么想法或解决方法吗?

4

7 回答 7

11
  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,以确保在所有情况下都释放对象
于 2009-06-10T12:05:08.283 回答
3

为什么不在回调中处理客户端?

于 2009-06-10T11:13:29.743 回答
1

我有几个想法:

  1. 改变你的架构。
  2. 在处理程序中处置
  3. 使用 EventWaitHandle
于 2009-06-10T11:12:48.030 回答
0

如果注册了事件处理程序,那么当有可以在其上调用的事件时,您无法真正处置该对象。您最好的选择是使包含类成为一次性并将客户端存储在类变量中,以便在包含类存在时进行处置。

就像是

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();
    }
}
于 2009-06-10T11:14:32.627 回答
0

好吧,处置一个对象用于杀死在 GC(最终)到来并收集您的对象之前您不希望持有的资源。你的 dispose 方法会杀死你需要的任何东西client_PreloadCompleted吗?

当所有预期的回调都发生时,您可以让对象自行处理:为您期望的每个回调保留一个“引用计数器”,并在每个发生的回调上递减 - 检查回调处理程序末尾是否为 null,如果是,则处理。

其他解决方法:不用担心IDisposable. GC 会收集你的对象。您可能不希望回调处理程序(可能不会被触发)处于临界状态。它(回调)应该在调用它时打开它需要的任何资源,然后关闭它们。

于 2009-06-10T11:15:35.310 回答
0

异步等待和确定性处理不能很好地混合。如果你能找到一种拆分代码的方法,使得一次性的东西进入一个类,而事件进入另一个类,那会让一切变得更简单。

于 2009-06-10T11:21:56.357 回答
0

为什么不在client_PreloadCompleted方法中配置?与thecoop 提供的类似Dispose,在您从客户端对象内部访问所有需要的数据之后,只需在上述方法中调用即可。

编辑:我认为这也是 orialmog 提供的。

于 2009-06-10T11:30:11.037 回答