Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/322.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何处理调用了异步方法的对象?_C#_.net_Asynchronous_Dispose_Idisposable - Fatal编程技术网

C# 如何处理调用了异步方法的对象?

C# 如何处理调用了异步方法的对象?,c#,.net,asynchronous,dispose,idisposable,C#,.net,Asynchronous,Dispose,Idisposable,我有一个对象PreloadClient,它实现了IDisposable,我想处理它,但是在异步方法完成调用之后。。。但事实并非如此 private void Preload(SlideHandler slide) { using(PreloadClient client = new PreloadClient()) { client.PreloadCompleted += client_P

我有一个对象
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)
    }
那么,有什么想法或解决办法吗?

我有一些想法:

  • 改变你的架构
  • 在处理程序中处理
  • 使用EventWaitHandle

  • 为什么不在回调中释放客户端?

    如果有正在注册的事件处理程序,那么当对象上存在可以调用的事件时,您无法真正释放该对象。最好的办法是使包含类成为一次性的&将客户机存储在一个类变量中,当包含类被释放时将被释放

    差不多

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

    好的,处理一个对象是用来杀死你不想保留的资源,直到GC(最终)来收集你的对象。您的dispose方法是否删除了
    客户端中所需的任何内容

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


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

    异步等待和确定性处理不能很好地混合。如果你能找到一种分割代码的方法,这样一次性的东西可以放在一个类中,而事件可以放在另一个类中,那么一切都会变得更简单。

    为什么不在
    客户端\u
    方法中处理呢? 与客户机提供的类似,在从客户机对象内部访问所有需要的数据之后,只需在上述方法中调用
    Dispose

    编辑:我想这也是orialmog提供的

  • 您不应该使用
    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);
    }
    
  • 请注意,此实现不是线程安全的

    • 必须访问
      \u activeClients
      列表,因为从不同的线程调用了
      preload completed
      方法
    • 您的包含对象可能在客户端触发事件之前被释放。在这种情况下,“做事”应该什么都不做,所以这是你应该注意的另一件事
    • 最好在事件处理程序中使用
      try
      /
      finally
      块,以确保在所有情况下都释放该对象

  • 坏主意。现在,您将containingclass的生存期和客户端的生存期绑定起来。如果多次调用Preload会怎么样?你不会处置你的客户。如果在异步操作完成之前调用容器上的dispose会怎么样?你很可能会得到丑陋的例外检查关于EventWaitHandle的这一点:如果你有2个或更多的事件。。。您选择哪一个进行处理:)?@jalchr:如果您正确地实现了“dispose”,则无论调用了多少次“dispose”,多次调用dispose都不会影响结果。@jalchr:如果您需要在最后一次回调时处理对象,使用一些锁或互锁保护字段来跟踪有多少回调未完成,并让每个回调检查是否是最后一个回调,如果是,请进行处理。请详细说明。。。你把事件放在另一节课上是什么意思?假设一个类持有需要确定性清理的状态。那个班是IDisposable的好人选。但是,如果对其调用异步方法,则必须等待异步方法完成,然后才能对其进行处理。为什么要麻烦?您还可以将其设置为同步方法,并在该方法完成时处置该对象。你在同样的时间内完成了同样的事情。我相信这是一个很好的回答,在这里似乎是适用的,而且我希望看到更经常使用的模式是,让客户机包括一个
    IsDisposed
    属性,让
    \u activeClients
    为客户机保存一个
    WeakReference
    列表,并将一个项目添加到
    \u activeClients
    中,检查两个项目,查看
    WeakReference
    是否已死亡或目标是否已被处置,如果是,则将其与下一个项目“交换”。这将避免在异步回调中锁定列表,并且在添加项时,将从列表中删除每个项的O(N)成本替换为O(1)额外成本。@niico您只能在单个块(即单个子句)内使用
    using
    。这意味着一旦
    using
    块结束,对象将被释放。在本例中,OP调用了一个异步
    Preload
    方法,因此他/她希望延迟处理,直到所有异步工作完成。
    protected override Dispose(bool disposing)
    {
        foreach (PreloadClient client in _activeClients)
        { 
            client.PreloadCompleted -= client_PreloadCompleted;
            client.Dispose();
        }
        _activeClients.Clear();
        base.Dispose(disposing);
    }