【问题标题】:Is there a way to find a control's owner thread?有没有办法找到控件的所有者线程?
【发布时间】:2009-11-02 08:25:00
【问题描述】:

我正在开发的大型应用程序遇到一些线程问题(出现跨线程异常)。有没有办法找到创建特定控件的线程名称/ID?

当我尝试将新控件添加到我的控件的控件集合时发生错误。我真的无法创建一个小的、可重复的样本,所以我会尽可能地描述它。

我有一个位于窗体上的主控件,称为 _mainControl。在它的构造函数中,我实例化了另一个控件的实例,例如

ChildControl _childControl = new ChildControl();

现在 _childControl 存在,但我还没有将它添加到 _mainControls 集合中。

最终,_mainControl 收到我应该添加控件的事件通知。在事件处理程序中,我检查是否 this.InvokeRequired,如果是,我调用处理程序,如下所示:

AddControlEventHander(...)
{
    if(InvokeRequired)
    {
        BeginInvoke(new MethodInvoker(AddControlEventHander);
        return;
    }
    Controls.Add(_childControl);
}

异常总是在 Controls.Add 处引发(“跨线程操作无效:控件‘_item’是从创建它的线程以外的线程访问的”)。

现在,我不明白这怎么可能。我在创建 _mainControl 的同一线程上创建了 _childControl。当我在调试时查看线程窗口时,当我调用 Control.Add 时,当前线程名称/id 与添加 _childControl 时相同。然而,最让我困惑的是来自 _mainControl 的以下调用:

InvokeReuqired == false;
_childControl.InvokeRequired == false;
_childControl._item.InvokeRequired == true; //I made _item public just to try this and it returns true!

这怎么可能? _childControl 是否可以在一个线程上创建,而其子项以某种方式在另一个线程上创建? _childControl 的所有子项都是在初始化期间创建的,正如通常所做的那样。

如果有人对可能发生的事情有任何提示/建议,请告诉我。

谢谢。

编辑:

如果有人感兴趣,我会发现发生了什么。我很好奇如何在一个线程上创建控件,并且即使 InitializeComponent 都是在同一个线程上完成的,它也是在另一个线程上创建的子控件。所以,我发现孩子是在哪个线程上创建的,使用类似于下面查尔斯建议的代码。一旦我知道了这一点,我至少知道要关注哪个线程。然后我打破了子控件的 OnHandleCreated 事件,发现了问题。

我不知道的一件事是,控件的句柄是在控件第一次可见时创建的,而不是在创建时创建的。因此,不拥有该控件的线程试图将其可见性设置为 true。所以我添加了一个检查来查看 InvokeRequired 是否可以解决问题。然而,我真的没想到的是调用 InvokeRequired 将创建控件的句柄,如果它还没有创建!这实际上会导致在错误的线程上创建控件,并始终为 InvokeRequired 返回 false。我通过触摸控件的 Handle 属性解决了这个问题,以便在调用 InvokeRequired 之前创建它。

感谢大家的帮助:)

【问题讨论】:

标签: c# multithreading


【解决方案1】:

要获取控件的所有者线程,请尝试以下操作:

private Thread GetControlOwnerThread(Control ctrl)
{
    if (ctrl.InvokeRequired)
        ctrl.BeginInvoke(
            new Action<Control>(GetControlOwnerThread),
            new object[] {ctrl});
    else
        return System.Threading.Thread.CurrentThread;
}

子控件可以与父控件(容器控件)在不同的线程上吗?是的,这完全取决于构造控件时正在运行的线程(新建)

您总是必须检查 InvokeRequired...因为您永远不知道哪个线程可能会调用您正在编码的方法...您是否需要为每个子控件分别检查 InvokeRequired,取决于您对所有这些的确定程度控件是否在同一个线程上创建。如果所有控件都是在创建表单时在同一个初始化例程中创建的,那么您可能可以安全地假设它们都是在同一个线程上创建的

【讨论】:

  • 嗯。那么,当您尝试查看控件是否需要 InvokeRequired 时,需要进行哪些检查?您是否总是需要检查控件及其所有子控件的 InvokeRequired?
  • 是的,您总是必须检查 InvokeRequired... 因为您永远不知道哪个线程可能会调用您正在编码的方法... 您是否需要为每个子控件单独检查 InvokeRequired,取决于关于您是否确定所有控件是否在同一个线程上创建。如果所有控件都是在创建表单时在同一个初始化例程中创建的,那么您可能可以安全地假设它们都是在同一个线程上创建的。
  • 这对我来说似乎很奇怪。据我所知,_childControl 及其所有子项看起来像是在同一个线程上创建的。此外,如果 _childControls 子项以某种方式在不同的线程上创建,我看不到如何调用 _mainControl 的 Controls.Add,因为在创建 _childControl 句柄时由于跨线程而失败。由于 _childControl 或它的孩子,我失败了。我不明白这怎么可能。我将重新审视它并进行一些重构。也许它会消失:)
  • 如果在 threadA 上创建了一个容器控件,并且在另一个线程(例如 threadB)上创建了一些其他控件,那么您应该能够将该控件添加到 threadA 上的容器的 COControls 集合中,即线程容器创建于。您正在修改容器控件,而不是子控件。
  • 不应该 Action&lt;Control&gt;Func&lt;Control, Thread&gt; 因为 Action 没有返回值? BeginInvoke 也不应该是 Invoke 因为 BeginInvoke 没有返回值?也不应该像return (Thread)ctrl.Invoke...一样开始吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-07
  • 2021-09-12
  • 1970-01-01
  • 2012-10-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多