线程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 = ""; } }