Note that there are some explanatory texts on larger screens.

plurals
  1. POHow to correctly dispose a Form, without risk of an Invoke being called from another thread on a disposed object?
    primarykey
    data
    text
    <p>I have a Form which "listens" to events that are raised elsewhere (not on the Form itself, nor one of its child controls). Events are raised by objects which exist even after the Form is disposed, and may be raised in threads other than the one on which the Form handle was created, meaning I need to do an Invoke in the event handler (to show the change on the form, for example).</p> <p>In the <code>Dispose(bool)</code> method of the form (overridden) I unsubscribed from all events that may still be subscribed when this method is called. However, Invoke is still called sometimes from one of the event handlers. I assume this is because the event handler gets called just a moment before the event is unsubscribed, then OS switches control to the dispose method which executes, and then returns control back to the handler which calls the Invoke method on a disposed object.</p> <p>Locking the threads doesn't help because a call to Invoke will lock the calling thread until main thread processes the invoked method. This may never happen, because the main thread itself may be waiting for a release of the lock on the object that the Invoke-calling thread has taken, thus creating a deadlock.</p> <p>So, in short, how do I correctly dispose of a Form, when it is subscribed to external events, which may be raised in different threads?</p> <p>Here's how some key methods look at the moment. This approach is suffering the problems I described above, but I'm not sure how to correct them.</p> <p>This is an event handler handling a change of Data part of the model:</p> <pre><code>private void updateData() { if (model != null &amp;&amp; model.Data != null) { model.Data.SomeDataChanged -= new MyEventHandler(updateSomeData); model.Data.SomeDataChanged += new MyEventHandler(updateSomeData); } updateSomeData(); } </code></pre> <p>This is an event handler which must make changes to the view:</p> <pre><code>private void updateSomeData() { if (this.InvokeRequired) this.myInvoke(new MethodInvoker(updateSomeData)); else { // do the necessary changes } } </code></pre> <p>And the myInvoke method:</p> <pre><code>private object myInvoke(Delegate method) { object res = null; lock (lockObject) { if (!this.IsDisposed) res = this.Invoke(method); } return res; } </code></pre> <p>My override of the <code>Dispose(bool)</code> method:</p> <pre><code>protected override void Dispose(bool disposing) { lock (lockObject) { if (disposing) { if (model != null) { if (model.Data != null) { model.Data.SomeDataChanged -= new MyEventHandler(updateSomeData); } // unsubscribe other events, omitted for brevity } if (components != null) { components.Dispose(); } } base.Dispose(disposing); } } </code></pre> <p>Update (as per Alan's request):</p> <p>I never explicitly call the Dispose method, I let that be done by the framework. The deadlock has so far only happened when the application is closed. Before I did the locking I sometimes got some exceptions thrown when a form was simply closed.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload