![](/img/trans.png)
[英]Reliable/easy way to replace Windows.Forms control with a derived control?
[英]Windows Forms: Is there a way to wait for all pending Invokes to a Control to end?
我需要这样做以解决僵局。 我的Windows窗体控件有对C ++ / CLI类的引用,该类包装了C ++本机类。 本机类对C ++ / CLI类进行回调,该回调将它们映射到表单处理的事件。 从始终运行的线程中调用这些回调。
当我想处置控件时,我注销所有事件,以使本机类不再能回调。 完成此操作后,我将处置C ++ / CLI包装器,这反过来会破坏本机类。 在本机类析构函数中,我使用Windows事件通知线程结束,并无限期地等待线程结束。
但是,如果在处置开始时线程位于回调的中间,则可能在Control.Invoke
中将其停止,并随后发生死锁。 因此,标题问题。 这可能吗? 如果是这样,我可以这样进行:
Invoke
均已完成,因此无法用Invoke
造成死锁,并且由于未注册事件而无法解雇它们) 我愿意接受其他解决此问题的建议
选项1:在删除事件处理程序之后但在析构函数之前,通过Application.DoEvents处理挂起的回调。 请注意,文档说这可以处理Windows消息,并且没有提及是否强制处理调用。
选项2:不阻止事件线程:通过BeginInvoke调用表单回调。 请注意,在C ++对象被销毁后,您可能会得到事件,因此,请向事件处理程序中添加其他逻辑,以确保C ++对象未被销毁。
FYI, BeginInvoke
和Invoke
都使用此功能。 BeginInvoke
将synchronous
设置为false。 请注意,我稍微修改了此方法以删除不感兴趣的部分:
private object MarshaledInvoke(Control caller, Delegate method, object[] args, bool synchronous)
{
ThreadMethodEntry entry = new ThreadMethodEntry(caller, method, args, synchronous, executionContext);
lock (this.threadCallbackList)
{
if (threadCallbackMessage == 0)
threadCallbackMessage = SafeNativeMethods.RegisterWindowMessage(Application.WindowMessagesVersion + "_ThreadCallbackMessage");
this.threadCallbackList.Enqueue(entry);
}
UnsafeNativeMethods.PostMessage(new HandleRef(this, this.Handle), threadCallbackMessage, IntPtr.Zero, IntPtr.Zero);
// BeginInvoke just takes this branch instead of waiting for completion
if (!synchronous)
return entry;
if (!entry.IsCompleted)
this.WaitForWaitHandle(entry.AsyncWaitHandle);
if (entry.exception != null)
throw entry.exception;
return entry.retVal;
}
没有可能响应表单关闭事件并等待线程完成。 有一个无法解决的线程竞争条件。 死锁的可能性非常高,DoEvents可以打破死锁,但是太丑陋了。
实现FormClosing事件,并告诉类停止运行其线程。 并取消关闭 。 线程应该在退出之前引发一个事件。 在事件处理程序中使用BeginInvoke()封送对主线程的调用。
现在,您可以保证所有调用均已完成,因为它们是有序的。 此外,您可以保证线程不再生成任何事件,因此不再有调用发生,并且取消订阅事件是安全的(实际上不是必需的)。 设置一个标志,表明现在可以进行真正的关闭,并调用Close()来实际上关闭窗体。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.