简体   繁体   English

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

[英]Windows Forms: Is there a way to wait for all pending Invokes to a Control to end?

I need to do this in order to solve a deadlock. 我需要这样做以解决僵局。 My Windows Forms Control has a reference to a C++/CLI class which wraps a C++ native class. 我的Windows窗体控件有对C ++ / CLI类的引用,该类包装了C ++本机类。 The native class makes callbacks to the C++/CLI class, which maps them to events handled by the form. 本机类对C ++ / CLI类进行回调,该回调将它们映射到表单处理的事件。 These callbacks are called from a thread which runs all the time. 从始终运行的线程中调用这些回调。

When I want to dispose the control, I unregister all events, so that the native class can't call back anymore. 当我想处置控件时,我注销所有事件,以使本机类不再能回调。 Once that's done, I dispose the C++/CLI wrapper, which in turn destroys the native class. 完成此操作后,我将处置C ++ / CLI包装器,这反过来会破坏本机类。 In the native class destructor, I signal the thread to end using a Windows event, and wait indefinitely for the thread to end. 在本机类析构函数中,我使用Windows事件通知线程结束,并无限期地等待线程结束。

However, if the thread was in the middle of a callback when the disposal starts, he might be stopped in a Control.Invoke , and deadlock ensues. 但是,如果在处置开始时线程位于回调的中间,则可能在Control.Invoke中将其停止,并随后发生死锁。 Hence the title question. 因此,标题问题。 Is this possible? 这可能吗? If it were so, I could proceed this way: 如果是这样,我可以这样进行:

  • Unregister all events (native thread won't be able to call back anymore) 取消注册所有事件(本地线程将无法再回调)
  • Wait for all pending .Invokes to finish 等待所有待处理的调用完成
  • Dispose C++/CLI wrapper 处置C ++ / CLI包装器
    • Destroy C++ native class 销毁C ++本机类
      • Signal thread to end 信号线程结束
      • Wait for thread to end (can't deadlock with an Invoke since all of those finished and no more of them could have been fired since events were unregistered) 等待线程结束(由于所有Invoke均已完成,因此无法用Invoke造成死锁,并且由于未注册事件而无法解雇它们)
  • Bliss

I'm open to other suggestions for solving this problem 我愿意接受其他解决此问题的建议

Option 1: Process pending callbacks via Application.DoEvents after removing the event handlers but before the destructor. 选项1:在删除事件处理程序之后但在析构函数之前,通过Application.DoEvents处理挂起的回调。 Note the documentation says this handles Windows messages and does not mention if this forces processing of Invokes. 请注意,文档说这可以处理Windows消息,并且没有提及是否强制处理调用。

Option 2: Do not block the event thread: Call your form callbacks via BeginInvoke. 选项2:不阻止事件线程:通过BeginInvoke调用表单回调。 Note that you might get events after the C++ object is destroyed, so add additional logic to the event handlers to ensure the C++ object has not been destroyed. 请注意,在C ++对象被销毁后,您可能会得到事件,因此,请向事件处理程序中添加其他逻辑,以确保C ++对象未被销毁。

FYI, BeginInvoke and Invoke both use this function. FYI, BeginInvokeInvoke都使用此功能。 BeginInvoke sets synchronous to false. BeginInvokesynchronous设置为false。 Note that I hacked this method up a bit to remove the uninteresting portions: 请注意,我稍微修改了此方法以删除不感兴趣的部分:

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;
}

There is no possibility of responding to the form closing event and wait for the thread to finish. 没有可能响应表单关闭事件并等待线程完成。 There's an unsolvable threading race condition. 有一个无法解决的线程竞争条件。 And very good odds for deadlock, DoEvents would break it but is too ugly. 死锁的可能性非常高,DoEvents可以打破死锁,但是太丑陋了。

Implement the FormClosing event and tell the class to stop running its thread. 实现FormClosing事件,并告诉类停止运行其线程。 And cancel the close . 取消关闭 The thread should raise an event just before it exits. 线程应该在退出之前引发一个事件。 Use BeginInvoke() in the event handler to marshal that call to the main thread. 在事件处理程序中使用BeginInvoke()封送对主线程的调用。

Now you have a guarantee that all invokes are completed since they are ordered. 现在,您可以保证所有调用均已完成,因为它们是有序的。 Furthermore, you have a guarantee that the thread can no longer generate any more events so no more invokes are coming and it is safe to unsubscribe the events (not actually necessary). 此外,您可以保证线程不再生成任何事件,因此不再有调用发生,并且取消订阅事件是安全的(实际上不是必需的)。 Set a flag that a real close is now possible and call Close() to actually close the form. 设置一个标志,表明现在可以进行真正的关闭,并调用Close()来实际上关闭窗体。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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