【问题标题】:Deadlock when invoking the UI thread from a worker thread从工作线程调用 UI 线程时出现死锁
【发布时间】:2010-09-21 16:48:18
【问题描述】:

当我从工作线程调用 UI 线程时出现死锁。事实上,工作线程在调用行被阻塞:

return (ucAvancementTrtFamille)mInterfaceTraitement.Invoke(d, new object[] { psFamille });

奇怪的是 UI 线程(如果我错了,请纠正我,它是主线程)是空闲的。

有什么办法:

  1. 看看我实际上试图调用哪个线程?
  2. 看看所说的线程到底在做什么?

我们可以在下图中看到,工作线程(ID 3732)阻塞在 Invoke 行,而 MainThread 在应用程序的 main 函数中处于空闲状态。

编辑:这里是主线程的堆栈:

Edit2:实际上,我再次暂停了程序,堆栈如下所示:

Edit3:找到解决方法

我终于找到了解决方法。问题显然是由于异步包装竞争 条件问题。解决方法是使用 BeginInvoke 并等待它超时。当它超时时,再次调用它并循环直到它最终返回。大多数情况下,它实际上在第二次调用时起作用。

IAsyncResult ar = mInterfaceTraitement.BeginInvoke(d, new object[] { psFamille });
            while (!ar.AsyncWaitHandle.WaitOne(3000, false))
            {
                ar = mInterfaceTraitement.BeginInvoke(d, new object[] { psFamille });
            }
            // Async call has returned - get response
            ucAvancementTrtFamille mucAvancementTrtFamille = (ucAvancementTrtFamille)mInterfaceTraitement.EndInvoke(ar);

它并不漂亮,但这是我找到的唯一解决方案。

【问题讨论】:

  • 你能发布 d 代表在做什么吗?
  • 这是一个 WinForms 或 WPF 应用程序,对吧?
  • 委托只是创建了一个图形控件(此时它甚至没有将它添加到任何表单中)。我使用 WinForms。
  • UI线程是否有可能随时等待工作线程?

标签: c# .net multithreading deadlock invoke


【解决方案1】:

主线程看起来并不空闲。您的屏幕截图显示了它在 ECM.Program.Main 的当前位置。这不可能是正确的,如果它是空闲的,那么它在 Application.Run() 内部,泵送消息循环。这是 Invoke() 完成所必需的。

双击主线程并切换到调用堆栈窗口,看看它到底在做什么。

【讨论】:

  • 嗯,你是对的:它在 Application.Run() 中,它在我的 ECM.Program.Main 中。查看我的编辑:它显示了主线程的堆栈。
  • 是的,看起来我也很兴奋。粗糙的。设置符号服务器,启用非托管代码调试并向我们展示死锁线程的调用堆栈。
  • 似乎异步包装器存在竞争条件问题。使用我找到的解决方法查看我的第三次编辑。
【解决方案2】:

您是否尝试过使用BeginInvoke 而不是InvokeBeginInvoke 是异步的。

【讨论】:

  • 是的,但BeginInvoke 不会阻止来电者。这只是一个建议,虽然是的,但这不重要。
  • SimpleCoder,OP 想要 Invoke 的返回值。 BeginInvoke 不能只替换这里。
【解决方案3】:

你是对的。主线程是应用程序的入口点,通常是调用Application.Run 的位置,它使消息循环继续进行。所以这应该是 UI 线程,除非你在消息循环方面做了一些不寻常的事情,这不太可能。

在线程窗口中,您可以右键单击主线程并选择切换以将调试上下文更改为该线程。调用堆栈窗口将显示当前执行方法的位置。

如果您的工作线程确实在 Control.Invoke 调用上被阻塞,并且 UI 线程如您所声称的那样处于空闲状态,那么问题可能在于正在编组的委托中执行指令或消息循环尚未执行已启动。后者似乎是合理的,因为您的屏幕将主线程的位置显示为Main

【讨论】:

  • 我正在编辑我的问题,并添加主线程的堆栈。我已启用“显示外部代码”选项。
【解决方案4】:

您是否尝试过使用 BackgroundWorker。如果您使用它,它将为您节省很多 this.Invoke 和 InvokeRequired 调用。
我相信,如果您创建一个新线程并使其执行您所显示的行,您将不会遇到死锁。我会尝试找到我的旧代码并将其发布在这里。

【讨论】:

    【解决方案5】:

    您使用的是 Visual Studio 2008 吗? 如果答案是肯定的,您应该尝试使用 Visual Studio 2010。这是一个已知错误。

    祝你好运!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多