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)
}
那么,有什么想法或解决办法吗?我有一些想法:
为什么不在回调中释放客户端?如果有正在注册的事件处理程序,那么当对象上存在可以调用的事件时,您无法真正释放该对象。最好的办法是使包含类成为一次性的&将客户机存储在一个类变量中,当包含类被释放时将被释放 差不多
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);
}