【发布时间】: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 之前创建它。
感谢大家的帮助:)
【问题讨论】:
-
感谢您的提示。我同样发现无害的
if (control.Handle != null) ...实际上在该线程上创建了控件!
标签: c# multithreading