繁体   English   中英

Windows窗体:是否有一种方法可以等待对控件的所有未决调用结束?

[英]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中将其停止,并随后发生死锁。 因此,标题问题。 这可能吗? 如果是这样,我可以这样进行:

  • 取消注册所有事件(本地线程将无法再回调)
  • 等待所有待处理的调用完成
  • 处置C ++ / CLI包装器
    • 销毁C ++本机类
      • 信号线程结束
      • 等待线程结束(由于所有Invoke均已完成,因此无法用Invoke造成死锁,并且由于未注册事件而无法解雇它们)

我愿意接受其他解决此问题的建议

选项1:在删除事件处理程序之后但在析构函数之前,通过Application.DoEvents处理挂起的回调。 请注意,文档说这可以处理Windows消息,并且没有提及是否强制处理调用。

选项2:不阻止事件线程:通过BeginInvoke调用表单回调。 请注意,在C ++对象被销毁后,您可能会得到事件,因此,请向事件处理程序中添加其他逻辑,以确保C ++对象未被销毁。

FYI, BeginInvokeInvoke都使用此功能。 BeginInvokesynchronous设置为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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM