I have a form that "listens" for events that occur elsewhere (not on the Form itself, not on any of its child controls). Events are created by objects that exist even after the Form is placed, and can occur in streams other than those in which the form handle was created, which means that I need to execute Invoke in the event handler (to show a change to the form, for example).
In the Dispose(bool) method of the (overridden Dispose(bool) form, I unsubscribed from all events that can still be signed when this method is called. However, Invoke is still sometimes called from one of the event handlers. I assume that this is because the event handler is called just a minute before the event is canceled, and then the OS will switch to the control method that executes and then return control to the handler that calls the Invoke method on the remote object.
Blocking threads does not help, because invoke calls block the calling thread until the main thread processes the called method. This may never happen, because the main thread itself can wait to release the lock on the object that was called by the thread of the calling call, thereby creating a deadlock.
So, in short, how do I properly manage a form when it subscribes to external events that may occur in different threads?
Here are some key methods. This approach suffers from the problems described above, but I'm not sure how to fix them.
This is an event handler that handles a change in a piece of model data:
private void updateData() { if (model != null && model.Data != null) { model.Data.SomeDataChanged -= new MyEventHandler(updateSomeData); model.Data.SomeDataChanged += new MyEventHandler(updateSomeData); } updateSomeData(); }
This is an event handler that should make changes to the view:
private void updateSomeData() { if (this.InvokeRequired) this.myInvoke(new MethodInvoker(updateSomeData)); else {
And the myInvoke method:
private object myInvoke(Delegate method) { object res = null; lock (lockObject) { if (!this.IsDisposed) res = this.Invoke(method); } return res; }
My redefinition of the Dispose(bool) method:
protected override void Dispose(bool disposing) { lock (lockObject) { if (disposing) { if (model != null) { if (model.Data != null) { model.Data.SomeDataChanged -= new MyEventHandler(updateSomeData); }
Update (as requested by Alan):
I never explicitly call the Dispose method, I assume that this is done using the framework. The deadlock so far has only been when the application is closed. Before I made the lock, I sometimes got some exceptions that were thrown when the form was just closed.