如何处理具有异步方法的对象?
我有这个对象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) }
那么,任何想法或工作?
-
您不应该使用
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
_activeClients = new List (); 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); } -
在这种情况下,您必须在处置主类时处置所有客户端:
protected override Dispose(bool disposing) { foreach (PreloadClient client in _activeClients) { client.PreloadCompleted -= client_PreloadCompleted; client.Dispose(); } _activeClients.Clear(); base.Dispose(disposing); }
-
请注意,此实现不是线程安全的
- 访问
_activeClients
列表必须是线程安全的 ,因为从另一个线程调用PreloadCompleted
方法 - 您的包含对象可能会在客户端触发事件之前处理。 在这种情况下,“做东西”应该什么也不做,所以这是你应该照顾的另一件事。
- 在事件处理程序中使用
try
/finally
块可能是个好主意,以确保在所有情况下都可以处置该对象
- 访问
为什么不在回调中处置客户端?
我有一些想法:
- 改变你的架构。
- 处理在处理程序中
- 使用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方法是否会在client_PreloadCompleted
杀死您需要的任何内容?
当所有预期的回调都发生时,您可以让对象自行处理:为您期望的每个回调保留一个“引用计数器”,并在每次回调时减少该回调 – 在回调处理程序结束时检查为null,如果是,则进行处理。
其他解决方法:不要担心IDisposable
。 GC将收集您的对象。 您可能不希望回调处理程序(可能不会被触发)具有临界状态。 它(回调)应该在调用它时打开它需要的任何资源然后关闭它们。
异步等待和确定性处理不能很好地混合。 如果你能找到一种方法来分割代码,使得一次性东西进入一个类,而事件进入另一个类,这将使一切变得更简单。
为什么不在client_PreloadCompleted
方法中进行处理? 类似于thecoop提供的内容,只需使用上述方法中的Dispose
调用,就可以从客户端对象中访问所有需要的数据。
编辑:我认为这也是orialmog提供的。