C#跨线程操作控件的最简单实现探究

Posted xh6300

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#跨线程操作控件的最简单实现探究相关的知识,希望对你有一定的参考价值。

随着程序复杂度的提高,程序不可避免会出现多个线程,此时就很可能存在跨线程操作控件的问题。

 

跨线程操作UI控件主要有三类方式:

1、禁止系统的线程间操作检查。(此法不建议使用

2、使用Invoke(同步)或者BeginInvoke(异步)。(使用委托实现,并用lambda表达式简化代码

3、使用BackgroundWorker组件。(此法暂不介绍,详情可见文末的参考资料

 

先看一个跨线程操作失败的例子:

新建一个Winform窗口程序项目,拖一个button1和label1控件到Form1窗体上。启动程序以后试图通过点击button1改变label1的值,完整代码如下:

 1 using System;
 2 using System.Threading;
 3 using System.Windows.Forms;
 4 
 5 namespace Windows跨线程调用控件
 6 {
 7     public partial class Form1 : Form
 8     {
 9         public Form1()
10         {
11             InitializeComponent();
12         }
13 
14         private void button1_Click(object sender, EventArgs e)
15         {
16             Thread thread1 = new Thread(new ParameterizedThreadStart(UpdateLabel));
17             thread1.Start("label已更新");
18         }
19         
20         private void UpdateLabel(object str)
21         {
22             label1.Text = str.ToString();
23         }
24     }
25 }

点击button1以后运行报错:

 

解决方案:

方法一:禁止系统的线程间操作检查。

代码就一句话:Control.CheckForIllegalCrossThreadCalls = false;通常写在Form1类的构造方法Form1()中。如下所示:

1 public Form1()
2         {
3             InitializeComponent();
4 
5             Control.CheckForIllegalCrossThreadCalls = false;         
6         }

但是,这种方法是很不可靠的,有时候还是会报错。

 

方法二:使用Invoke(同步)或者BeginInvoke(异步)最精简的代码如下:

 1 using System;
 2 using System.Threading;
 3 using System.Windows.Forms;
 4 
 5 namespace Windows跨线程调用控件
 6 {
 7     public partial class Form1 : Form
 8     {
 9         public Form1()
10         {
11             InitializeComponent();
12         }
13 
14         private void button1_Click(object sender, EventArgs e)
15         {
16             Thread thread1 = new Thread(UpdateLabel);//可以省略线程的委托类型ParameterizedThreadStart
17             thread1.Start("label已更新");
18         }
19 
20         private void UpdateLabel(object str)
21         {
22             if (label1.InvokeRequired)//不同线程为true,所以这里是true
23             {               
24                 BeginInvoke(new Action<string> (x => {label1.Text = x.ToString();}),str);    
25             }
26         }
27     }
28 }

说明:Action是.NET预定义好的委托,可以简化委托的语法,如果不清楚它的用法,可以搜索“Action和Func的用法”。

 

将上面的两种方法总结在同一段程序里面如下所示:(注意看代码中的注释)

 1 using System;
 2 using System.Threading;
 3 using System.Windows.Forms;
 4 
 5 namespace Windows跨线程调用控件
 6 {
 7     public partial class Form1 : Form
 8     {
 9         public Form1()
10         {
11             InitializeComponent();
12 
13             /************* 方法一 ************/
14             //Control.CheckForIllegalCrossThreadCalls = false;
15 
16         }
17 
18         private void button1_Click(object sender, EventArgs e)
19         {
20             Thread thread1 = new Thread(new ParameterizedThreadStart(UpdateLabel));
21             thread1.Start("label已更新");
22         }
23 
24 
25         //如果控件的 Handle(句柄) 是在与调用线程不同的线程上创建的(说明您必须通过 Invoke 方法对控件进行调用),则为 true;否则为 false。
26         private void UpdateLabel(object str)
27         {
28             if (label1.InvokeRequired)//当是不同的线程在访问时为true,所以这里是true
29             {
30                 /************* 方法二 ************/
31                 //Action<string> actionDelegate = (x) => { this.label1.Text = x.ToString(); };
32 
33                 ////如果已经创建控件的句柄,则除了 InvokeRequired 属性以外,控件上还有四个可以从【任何线程】上安全调用的方法,
34                 ////它们是:Invoke、BeginInvoke、EndInvoke 和 CreateGraphics。 
35                 ////在后台线程上创建控件的句柄之前调用 CreateGraphics 可能会导致非法的跨线程调用。 
36                 ////对于所有其他方法调用,则应使用调用 (invoke) 方法之一封送对控件的线程的调用。
37                 //this.label1.BeginInvoke(actionDelegate, str);
38 
39 
40                 /************* 方法二(变式) ************/
41                 //也可以直接用下面一句话来完成
42                 //Control.BeginInvoke 方法有两个重载:BeginInvoke(Delegate)    ,BeginInvoke(Delegate, Object[]),下式用的是第二个重载
43                 this.BeginInvoke(new Action<string>((x) => { label1.Text = x.ToString(); }), str);
44 
45                 //如果启动的多线程不需要带可变的参数,那更简单:
46                 //label1.BeginInvoke(new Action(() => { label1.Text = "aaa"; }));         
47             }
48         }
49     }
50 }

参考资料:

http://www.cnblogs.com/TankXiao/p/3348292.html

http://www.cnblogs.com/txw1958/archive/2012/08/21/csharp-crossthread-widget.html

以上是关于C#跨线程操作控件的最简单实现探究的主要内容,如果未能解决你的问题,请参考以下文章

C# Winform 跨线程更新UI控件常用方法汇总

c#如何跨线程调用窗体控件

C# Winform 跨线程更新UI控件常用方法总结(转)

C# winform 跨线程更改窗体控件的属性

c#中如何跨线程调用windows控件

实现Winform 跨线程安全访问UI控件