What you do not take into account is that the delegate passed to Invoke is called asynchronously in the user interface thread. The Invoke call sends a message to the forms message queue and picks up after a while.
What's happening:
UI Thread Background Thread Call update() take lock Call Invoke() Call update() release lock Call Dispose() take lock release lock
Instead of this:
UI Thread Background Thread Call update() take lock Call Invoke() block until UI Thread processes the message Process messages ... Dispose() wait for lock ****** Deadlock! ***** ... Call update() release lock
Because of this, the background thread may hold the lock while the user interface thread tries to start Dispose
The solution is much simpler than you tried. Because Invoke is sent asynchronously, there is no need to block.
private bool _disposed = false; private void update(object sender, EventArgs e) { if (InvokeRequired) { EventHandler handler = new EventHandler(update); Invoke(handler); return; } if (_disposed) return; label.Text = "blah"; } protected override void Dispose(bool disposing) { eventfullObject.OnUpdate -= update; _disposed = true;
The _disposed flag is only read or written in the user interface thread, so there is no need to block it. Now you call the stack like:
UI Thread Background Thread Call update() take lock Call Invoke() block until UI Thread processes the message Process messages ... Dispose() _disposed = true; ... Call update() _disposed is true so do nothing
source share