Control.BeginInvoke不会执行委托的原因?

Chr*_*ott 2 c# multithreading begininvoke winforms

概观

是否有解释Control.BeginInvoke()不执行它传递的委托?

代码示例

我们在Winforms应用程序中采用了以下模式,以便在UI线程上安全地执行UI相关工作:

private Control hiddenControl = new Control();

private void uiMethod()
{
  MethodInvoker uiDelegate = new MethodInvoker(delegate()
  {
    Logging.writeLine("Start of uiDelegate");
    //ui releated operations
    childDialog = new ChildDialog();
    childDialow.show();
    Logging.writeLine("End of uiDelegate");
  });

  if (hiddenControl.InvokeRequired)
  {
    Logging.writeLine("Start of InvokeRequired block");
    hiddenControl.BeginInvoke(uiDelegate);
    Logging.writeLine("End of InvokeRequired block");
  }
  else
  {
    uiDelegate();
  }
}
Run Code Online (Sandbox Code Playgroud)

在这里,我们显式创建一个控件"hiddenControl",以便在UI线程上运行委托.我们从不调用endInvoke,因为它显然不是 Control.BeginInvoke所必需的,我们永远不需要返回值,因为我们的方法只是操纵UI,无论如何.

虽然非常冗长,这种格局似乎是一个比较 接受的 解决方案.然而,有一些证据表明即使这种模式在所有情况下都不能很好地发挥作用.

意见

我不排除应用程序错误并指责WinForms.毕竟,选择可能不会破碎.然而,我无法解释为什么代表似乎根本没有参与竞选.

在我们的例子中,我们有时会观察到"开始uiDelegate"日志消息从未在某些线程场景中执行,即使"Start of InvokeReqiured block"和"End of InvokeRequired block"成功执行.

复制此行为非常困难,因为我们的应用程序是作为DLL提供的; 我们的客户在自己的应用程序中运行它.因此,我们无法保证可以调用这些方法的方式或方式.

我们排除了UI线程饥饿,因为观察到UI没有锁定.据推测,如果UI正在更新,则消息泵可操作并可用于从消息队列中提取消息并执行其委托.

摘要

鉴于此信息,我们可以尝试使这些调用更具防弹性吗?如前所述,我们对给定应用程序中的其他线程的控制相对较少,并且不控制调用这些方法的上下文.

还有什么可以影响委托成功传递给Control.BeginInvoke()的方式是否执行?

Yah*_*hia 7

根据MSDN, 即使在应该存在的情况下也InvokeRequired可以返回- 即在您创建该控件/表单(或其父节点)之前访问的情况下.falseInvokeRequiredtrueInvokeRequiredHandle

基本上,您的支票不完整,这会导致您看到的结果.

你需要检查IsHandleCreated- 如果那是false你那么你有麻烦因为Invoke/ BeginInvoke将是必要的但是因为Invoke/ BeginInvoke检查创建哪个线程Handle来做他们的魔法所以不会有效地工作...

只有当IsHandleCreatedtrue你根据什么行动InvokeRequired返回-沿着线的东西:

if (control.IsHandleCreated)
{
    if (control.InvokeRequired)
    {
        control.BeginInvoke(action);
    }
    else
    {
        action.Invoke();
    }
}
else 
{ 
    // in this case InvokeRequired might lie - you need to make sure that this never happens! 
    throw new Exception ( "Somehow Handle has not yet been created on the UI thread!" );
}
Run Code Online (Sandbox Code Playgroud)

因此,以下内容对于避免此问题非常重要

始终确保Handle在第一次访问UI线程以外的线程之前已创建.

根据MSDN,您只需要control.Handle在UI线程中引用以强制它被创建 - 在您的代码中,这必须在您第一次从任何不是UI线程的线程访问该控件/表单之前发生.

有关其他可能性,请参阅@JaredPar的答案.