如何处理具有异步方法的对象?

时间:2009-06-10 11:09:05

标签: c# .net asynchronous dispose idisposable

我有这个实现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 个答案:

答案 0 :(得分: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列表必须made thread-safe,因为您的PreloadCompleted方法是从其他线程调用的
    • 您的包含对象可能会在客户端触发事件之前处理。在那种情况下,“做东西”应该什么都不做,所以这是你应该照顾的另一件事。
    • 在事件处理程序中使用try / finally块可能是个好主意,以确保在所有情况下都可以处置该对象

答案 1 :(得分:3)

为什么不将客户端部署在回调中?

答案 2 :(得分:1)

我有一些想法:

  1. 改变你的架构。
  2. 处理处理程序
  3. 使用EventWaitHandle

答案 3 :(得分: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();
    }
}

答案 4 :(得分:0)

好吧,处理一个对象用于杀死你不想要的资源,直到GC(最终)到来并收集你的对象。您的dispose方法是否会在client_PreloadCompleted中杀死您需要的任何内容?

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

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

为什么不使用client_PreloadCompleted方法进行处理? 类似于thecoop提供的内容,只需使用上述方法中的Dispose调用,就可以从客户端对象中访问所有需要的数据。

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