在多线程应用中将会涉及不同的线程访问同一控件的问题,C#中禁止跨线程直接访问控件。某个控件在被创建时就记下了是谁创建了它,即它的创建线程。如果从另一个线程调用该控件,那么必须使用控件的 Invoke 方法来将调用封送现在调用它的线程。(Invoke方法是控件的方法)
到底是哪个线程要使用该控件呢?需要用InvokeRequired来询问一下,如果当前调用线程不是控件的创建线程,则为 true;否则为 false。
例子:
代码段前提是lb是一个label控件,在主线程创建(不用了解具体在哪个线程,反正和RunTimer不在一个线程),RunTimer方法是在另一个线程创建,这时候我们需要RunTimer方法调用主线程上的label控件。
private delegate void RunTimeDelegate(); private void RunTime() { if (lb.InvokeRequired) { RunTimeDelegate d = RunTime; lb.Invoke(d);//Invoke方法是控件的,这里相当于把RunTime方法交于了委托d, //d中相当于把RunTimer方法从线程中提取出来放到委托中,此时控件的Invoke方法就可以 // 调用委托中的和RunTimer相同方法,这样控件使用RunTimer方法就在控件的线程中使用的。 } else { time = endTime - TimeSpan.Parse(DateTime.Now.ToString("HH:mm:ss")); lb.Text = time.ToString(); } }
,这里添加几个实例,第一个是无参数传递,跨线程控制picturebox1控件
private delegate void DrawOnePicDelegate();//跨线程调用pictureBox1 public void DrawOnePicture() { if (pictureBox1.InvokeRequired)//如果控件被非本线程调用,则返回true { DrawOnePicDelegate d = new DrawOnePicDelegate (DrawOnePicture);//将方法放入委托 this.pictureBox1.Invoke(d);//从从控件创建线程中调用该方法 } else { //方法 }
另一个是带参数传递控制listbox的内容,传入的参数就是list中应该显示的text
delegate void AddItemCallback(string text); //这种方法演示如何在线程安全的模式下调用Windows窗体上的控件。 private void AddItem(string text) { if (this.listBox1.InvokeRequired) { AddItemCallback d = new AddItemCallback(AddItem); this.Invoke(d, new object[] { text }); } else { this.listBox1.Items.Add(text); } }
添加一个自己在实践中遇到的问题:问题大致描述:首先开辟一个线程,线程中方法实现在label控件上实现秒计数。
这里添加最开始错误的代码:
G_th = new System.Threading.Thread(ShowText); G_th.IsBackground = true;//设置线程为后台线程 G_th.Start();//开始执行线程 //以上为建立一个线程,线程名字为G_th。 private delegate void ShowTextDelegate(); public void ShowText() { int P_int_count = 0; while (true) { P_int_count = ++P_int_count > 1000000 ? 0 : P_int_count;//计数器累加 if (lb_time.InvokeRequired) { ShowTextDelegate d = new ShowTextDelegate(ShowText); lb_time.Invoke(d); } else { lb_time.Text = P_int_count.ToString(); } // Thread.Sleep(1000);//线程睡眠1秒 } }
上述代码运行时会出现卡死,原因在于while的循环问题,至于为什么,我也没搞懂,哪位大神看到了也可以帮小弟解答下,后来我改了代码成为下面的样子,运行成功。我把while的循环放在了外面,label的委托仅仅只判断label的引用线程,所以,在平时工作中,应当注意,尽量不要在控件的委托中调用循环。
G_th.IsBackground = true;//设置线程为后台线程 G_th.Start();//开始执行线程
public void AddCount() { P_int_count = 0; while (true) { P_int_count = ++P_int_count > 1000000 ? 0 : P_int_count;//计数器累加 ShowText(); Thread.Sleep(1000);//线程睡眠1秒 } } private delegate void ShowTextDelegate(); public void ShowText() { if (lb_time.InvokeRequired) { ShowTextDelegate d = new ShowTextDelegate(ShowText); lb_time.Invoke(d); } else { lb_time.Text = P_int_count.ToString();//窗体中显示计数 } }