2010-07-23 115 views
2

我有一个函数,它将控件添加到父控件,该控件从与创建控件的线程不同的线程调用。这是怎么一回事呢:即使在UI线程上执行时的跨线程操作

1  delegate void AddControlToParentDelegate(Control child, Control parent); 
2  private void addControlToParent(Control child, Control parent) { 
3  if (parent.InvokeRequired) { 
4   AddControlToParentDelegate d = new AddControlToParentDelegate(addControlToParent); 
5   this.Invoke(d, new object[] { child, parent }); 
6   } else { 
7    parent.Controls.Add(child); 
8   } 
9  } 
10 } 

这工作得很好,直到两parent.InvokeRequired以及child.InvokeRequired是真实的。然后,一旦执行第5行(现在代理d被调用并且该函数应该在UI线程上运行(对吗?))child在第7行会引发一个跨线程操作无效异常。为什么是这样?它不是已经在它创建的线程上运行了吗?

我设法通过增加一个额外(child.InvokeRequired)检查,以解决这个问题:

delegate void AddControlToParentDelegate(Control child, Control parent); 
private void addControlToParent(Control child, Control parent) { 
    if (parent.InvokeRequired) { 
     AddControlToParentDelegate d = new AddControlToParentDelegate(addControlToParent); 
     this.Invoke(d, new object[] { child, parent }); 
    } else { 
     if (child.InvokeRequired) { 
      this.Invoke(new MethodInvoker(delegate() { 
      parent.Controls.Add(child); 
      })); 
     } else { 
      parent.Controls.Add(child); 
     } 
    } 
} 

但这似乎只是可怕的黑客-Y和不必要的。这是做到这一点的方式吗?还是我完全错过了巴士?

回答

6

请注意,InvokeRequired在控件没有窗口句柄时不可靠。对于新创建的没有父级控制的子控件来说,这种情况几乎肯定会发生,这在您的“固定”代码中似乎就是这种情况。

见伊万Krivyakov的详细分析:http://www.ikriv.com/en/prog/info/dotnet/MysteriousHang.html

+3

此外,设计,*不*使用'InvokeRequired'是更易于维护。我建议为ui线程或使用“BackgroundWorker.ReportProgress”安排一个'Task',它们都是独立于平台的解决方案(即不绑定到Windows窗体)。 – 2010-07-23 01:42:21