线程Control.Invoke

我有一个function

public void ShowAllFly() { cbFly.Items.Clear(); cbFly.Items.Add("Uçuş Seçiniz..."); dsFlyTableAdapters.tblFlyTableAdapter _t=new KTHY.dsFlyTableAdapters.tblFlyTableAdapter(); dsFly _mds = new dsFly(); _mds.EnforceConstraints = false; dsFly.tblFlyDataTable _m = _mds.tblFly; _t.Fill(_m); foreach (DataRow _row in _m.Rows) { cbFly.Items.Add(_row["FlyID"].ToString()+"-"+_row["FlyName"].ToString() + "-" + _row["FlyDirection"].ToString() + "-" + _row["FlyDateTime"].ToString()); } _Thread.Abort(); timer1.Enabled = false; WaitPanel.Visible = false; } 

在Form_Load函数中像这样;

 { _Thread = new System.Threading.Thread(new System.Threading.ThreadStart(ShowAllFly)); _Thread.Start(); _Thread.Priority = System.Threading.ThreadPriority.Normal; } 

但是当我运行它;

在ShowAllFly函数中

 cbFly.Items.Clear(); ---- HERE Gives ERROR LIKE Control.Invoke must be used to interact with controls created on a separate thread. 

问题是什么?

在Windows窗体中有两条线程的黄金法则:

  • 除了创build控件的“句柄”之外的任何线程(通常只有一个UI线程),不要触摸任何控件属性或方法(除了那些明确列出为可以的)
  • 不要阻塞UI线程很长的时间,否则你会使应用程序无响应

为了从不同的线程与UI进行交互,您需要使用委托“调用”对UI线程的调用,并调用Control.Invoke / BeginInvoke 。 你可以testing你是否需要使用InvokeRequired属性来调用Invoke ,但是现在我个人倾向于这样做 – 无需在调用的时候没有太多的惩罚。

C#3中的Lambdaexpression式(或C#2中的匿名方法)使得这更好。

例如,你可以使用:

 cbFly.Invoke((MethodInvoker)(() => cbFly.Items.Clear())); 

所有的方括号都有一些方法,所以如果你使用的是C#3,你可能需要添加一个像这样的扩展方法:

 public static void Invoke(this Control control, MethodInvoker action) { control.Invoke(action); } 

那么你可以这样做:

 cbFly.Invoke(() => cbFly.Items.Clear()); 

这很简单。 通常情况下,您可以通过捕获任何需要在MethodInvoker中访问的variables来使用MethodInvoker

看到我的线程教程或Joe Albahari的更多细节。

作为次要的事情,我看到你正在使用Thread.Abort – 实际上是在你自己的线程中,尽pipe它之后还有其他的调用。 为什么? 放弃除你自己以外的任何线程是一个“只有紧急情况”types的调用(通常应该在应用程序被卸载之后),我看不到任何理由放弃当前的线程,当还有工作要做。 ..

另一个(ui)线程中的控件交互需要像这样调用:

 public delegate void ProcessResultDelegate(string result); void ProcessResult(string result) { if (textBox1.InvokeRequired) { var d = new ProcessResultDelegate(ProcessResult); d.Invoke(result); } else { textBox1.Text = result; } } 

我一直发现这篇文章对这个特定的问题很有帮助。

在你的例子中,你试图从一个没有创build控件的线程修改各种控件。 为了解决这个问题,你可以这样做(假设ShowAllFly()方法是你的表单中的一个方法):

 public void ShowAllFly() { Invoke((MethodsInvoker) delegate { cbFly.Items.Clear(); cbFly.Items.Add("Uçuş Seçiniz..."); dsFlyTableAdapters.tblFlyTableAdapter _t = new KTHY.dsFlyTableAdapters.tblFlyTableAdapter(); dsFly _mds = new dsFly(); _mds.EnforceConstraints = false; dsFly.tblFlyDataTable _m = _mds.tblFly; _t.Fill(_m); foreach (DataRow _row in _m.Rows) { cbFly.Items.Add(_row["FlyID"].ToString() + "-" + _row["FlyName"].ToString() + "-" + _row["FlyDirection"].ToString() + "-" + _row["FlyDateTime"].ToString()); } //_Thread.Abort(); // WHY ARE YOU TRYING TO DO THIS? timer1.Enabled = false; WaitPanel.Visible = false; } ); } 

只是为了强调一下@Jon Skeet所提出的观点,我已经提出了放弃线程的呼吁。 线程将自行结束。 没有理由以这种方式放弃它。

它必须被调用…但调用必须等待主线程我的意思是你不会得到这样的错误,但是这并不是完全并行工作,如果你想同时去多个进程只是创build多个线程

 Thread thread = new Thread(new delegate_method(method));//you must create delegate before thread.start (); Thread thread2 = new Thread(new delegate_method(method2));//you must create delegate before thread.start (); 

同时处理两个进程

  void method () { //do something here -- working background Remember can not control any UI control from here finish_thread() } void method2 () { //do something here -- working background Remember can not control any UI control from here finish_thread() } void finish_thread() { if(invoke.Required) { //Here you have to call delegate method here with UI BeginInvoke(new delegate_method(finish_thread)); } else { //Now you can control UI thread from here and also you finished background work //Do something working with UI thread textBox.Text = ""; } }