问题
I have this object PreloadClient
which implements IDisposable
, I want to dispose it, but after the asynchronous methods finish their call... which is not happening
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)
}
So, any ideas or work arounds ??
回答1:
You shouldn't use the
using
construct, but rather dispose your objects when they are no longer needed:// 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); }
in this case, you have to dispose all clients when disposing the main class:
protected override Dispose(bool disposing) { foreach (PreloadClient client in _activeClients) { client.PreloadCompleted -= client_PreloadCompleted; client.Dispose(); } _activeClients.Clear(); base.Dispose(disposing); }
Note that this implementation is not thread safe
- Access to the
_activeClients
list must be made thread-safe, as yourPreloadCompleted
method is called from a different thread - Your containing object may be disposed before a client fires the event. In that case "do stuff" should do nothing, so this is another thing you should take care of.
- It might be a good idea to use a
try
/finally
block inside your event handler, to make sure that the object gets disposed in all cases
- Access to the
回答2:
Why not dispose the client in the callback?
回答3:
I have a few ideas:
- change your architecture.
- dispose in the handler
- use EventWaitHandle
回答4:
If there are event handlers being registered, you can't really dispose the object while there are events that could be called on it. Your best bet is to make the containing class disposable & store the client in a class variable, to be disposed when the containing class is.
Something like
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();
}
}
回答5:
Well, disposing an object is used to kill resources that you don't want held until the GC (eventually) comes and collects your object. Does your dispose method kill anything you need in client_PreloadCompleted
?
You could have the object dispose itself, when all expected callbacks have happened: Keep a "reference counter" for each callback you are expecting and decrement that on each callback that happens - check for null at end of callback handler and dispose if so.
Other workaround: Don't worry about IDisposable
. GC will collect your object. You probably don't want a callback handler (that might not be fired) to have critical state. It (the callback) should just open any resources it needs when it is called and close them then.
回答6:
Asynchronous waits and deterministic disposal don't mix very well. If you can find a way of splitting the code such that the disposable stuff goes in one class and the events go in another, that would make everything simpler.
回答7:
Why not disposing in the client_PreloadCompleted
method?
Similar to what thecoop offered, just with the Dispose
call inside the above method, after you have accessed all the needed data from inside the client object.
Edit: I think that is what orialmog offered as well.
来源:https://stackoverflow.com/questions/974945/how-to-dispose-objects-having-asynchronous-methods-called