C#跨线程操作控件的最简单实现探究
随着程序复杂度的提高,程序不可避免会出现多个线程,此时就很可能存在跨线程操作控件的问题。
跨线程操作UI控件主要有三类方式:
1、禁止系统的线程间操作检查。(此法不建议使用)
2、使用Invoke(同步)或者BeginInvoke(异步)。(使用委托实现,并用lambda表达式简化代码)
3、使用BackgroundWorker组件。(此法暂不介绍,详情可见文末的参考资料)
先看一个跨线程操作失败的例子:
新建一个Winform窗口程序项目,拖一个button1和label1控件到Form1窗体上。启动程序以后试图通过点击button1改变label1的值,完整代码如下:
using System;
using System.Threading;
using System.Windows.Forms; namespace Windows跨线程调用控件
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
} private void button1_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(new ParameterizedThreadStart(UpdateLabel));
thread1.Start("label已更新");
} private void UpdateLabel(object str)
{
label1.Text = str.ToString();
}
}
}

点击button1以后运行报错:

解决方案:
方法一:禁止系统的线程间操作检查。
代码就一句话:Control.CheckForIllegalCrossThreadCalls = false;通常写在Form1类的构造方法Form1()中。如下所示:
public Form1()
{
InitializeComponent(); Control.CheckForIllegalCrossThreadCalls = false;
}
但是,这种方法是很不可靠的,有时候还是会报错。
方法二:使用Invoke(同步)或者BeginInvoke(异步)。最精简的代码如下:
using System;
using System.Threading;
using System.Windows.Forms; namespace Windows跨线程调用控件
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
} private void button1_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(UpdateLabel);//可以省略线程的委托类型ParameterizedThreadStart
thread1.Start("label已更新");
} private void UpdateLabel(object str)
{
if (label1.InvokeRequired)//不同线程为true,所以这里是true
{
BeginInvoke(new Action<string> (x => {label1.Text = x.ToString();}),str);
}
}
}
}
说明:Action是.NET预定义好的委托,可以简化委托的语法,如果不清楚它的用法,可以搜索“Action和Func的用法”。
将上面的两种方法总结在同一段程序里面如下所示:(注意看代码中的注释)
using System;
using System.Threading;
using System.Windows.Forms; namespace Windows跨线程调用控件
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent(); /************* 方法一 ************/
//Control.CheckForIllegalCrossThreadCalls = false; } private void button1_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(new ParameterizedThreadStart(UpdateLabel));
thread1.Start("label已更新");
} //如果控件的 Handle(句柄) 是在与调用线程不同的线程上创建的(说明您必须通过 Invoke 方法对控件进行调用),则为 true;否则为 false。
private void UpdateLabel(object str)
{
if (label1.InvokeRequired)//当是不同的线程在访问时为true,所以这里是true
{
/************* 方法二 ************/
//Action<string> actionDelegate = (x) => { this.label1.Text = x.ToString(); }; ////如果已经创建控件的句柄,则除了 InvokeRequired 属性以外,控件上还有四个可以从【任何线程】上安全调用的方法,
////它们是:Invoke、BeginInvoke、EndInvoke 和 CreateGraphics。
////在后台线程上创建控件的句柄之前调用 CreateGraphics 可能会导致非法的跨线程调用。
////对于所有其他方法调用,则应使用调用 (invoke) 方法之一封送对控件的线程的调用。
//this.label1.BeginInvoke(actionDelegate, str); /************* 方法二(变式) ************/
//也可以直接用下面一句话来完成
//Control.BeginInvoke 方法有两个重载:BeginInvoke(Delegate) ,BeginInvoke(Delegate, Object[]),下式用的是第二个重载
this.BeginInvoke(new Action<string>((x) => { label1.Text = x.ToString(); }), str);
//如果启动的多线程不需要带可变的参数,那更简单:
//label1.BeginInvoke(new Action(() => { label1.Text = "aaa"; }));
}
}
}
}
参考资料:
http://www.cnblogs.com/TankXiao/p/3348292.html
http://www.cnblogs.com/txw1958/archive/2012/08/21/csharp-crossthread-widget.html
C#跨线程操作控件的最简单实现探究的更多相关文章
- WinForm中新开一个线程操作 窗体上的控件(跨线程操作控件)
最近在做一个winform的小软件(抢票的...).登录窗体要从远程web页面获取一些数据,为了不阻塞登录窗体的显示,开了一个线程去加载数据远程的数据,会报一个错误"线程间操作无效: 从不是 ...
- C# 跨线程操作控件(简洁)
C# 跨线程操作控件 .net 原则上禁止跨线程访问控件,因为这样可能造成错误的发生.解决此问题的方法有两个: 第一 ...
- winform 跨线程操作控件
当进行winform的开发时,经常遇到用时比较久的操作,在传统的单线程程序中,用户必须等待这个耗时操作完成以后才能进行下一步的操作,这个时候,多线程编程就派上用场了,将这个耗时的操作放到一个新的子线程 ...
- C#跨线程操作控件
1.首先通过按键创建子线程: 创建子线程,子线程调用changeText方法. private void btnOK_Click(object sender, EventArgs e) { Threa ...
- 在.Net中进行跨线程的控件操作(下篇:BackgroundWorker)
在.Net中,如果我们在非UI线程上访问窗体上的控件的时候,会产生一个跨线程调用的异常,那么如何处理这种情况呢?在上一章中,我介绍了使用Control.Invoke方法,如果你不习惯使用委托,那么.N ...
- C# 跨线程调用控件
在C# 的应用程序开发中, 我们经常要把UI线程和工作线程分开,防止界面停止响应. 同时我们又需要在工作线程中更新UI界面上的控件, 下面介绍几种常用的方法 阅读目录 线程间操作无效 第一种办法:禁 ...
- winform跨线程访问控件
首先说下,.net 2.0以后加强了安全机制,不允许在winform中直接跨线程访问控件的属性.所以除了控件所在的线程外的线程调用会抛异常 (Cross-thread operation not va ...
- C# 关于跨线程访问控件问题
跨线程访问控件问题的原因是:控件都是在主线程中创建的,而系统默认控件的修改权归其创建线程所有.在子线程中如果需要直接修改控件的内容,需要使用委托机制将控件的修改操作交给主线程处理.因此,当没有使用委托 ...
- 【转载】C# 跨线程调用控件
转自:http://www.cnblogs.com/TankXiao/p/3348292.html 感谢原作者,转载以备后用 在C# 的应用程序开发中, 我们经常要把UI线程和工作线程分开,防止界面停 ...
随机推荐
- jdk1.8新特性之lambda表达式
lambda表达式其实就是指一个匿名函数,应用最广泛的就是匿名内部类的简化.在jdk1.8之前,我们定义一个匿名内部类可能需要写一大坨代码,现在有了lambda之后,可以写的很简洁了.但不是说lamb ...
- SmtpClient发送邮件
使用第三方SMTP服务器来发送邮件.如网易: SmtpClient sc = new SmtpClient("smtp.126.com"); sc.Credentials = ne ...
- mysql的三种安装方式(详细)
安装MySQL的方式常见的有三种: rpm包形式 通用二进制形式 源码编译 1,rpm包形式 (1) 操作系统发行商提供的 (2) MySQL官方提供的(版本更新,修复了更多常见BUG)www.mys ...
- 2DAY初识python
一.变量 1 什么是变量之声明变量 #变量名=变量值 age=18 gender1='male' gender2='female' 2 为什么要有变量 变量作用:“变”=>变化,“量”=> ...
- 关于PIC和FPGA
PIC:Peripheral Interface Controller. FPGA:Field Programmable Gate Array. 关于二者: 区别: FPGA是逻辑门器件,可以配置成为 ...
- 阻塞队列之四:ArrayBlockingQueue
一.ArrayBlockingQueue简介 一个由循环数组支持的有界阻塞队列.它的本质是一个基于数组的BlockingQueue的实现. 它的容纳大小是固定的.此队列按 FIFO(先进先出)原则对元 ...
- kotlin学习三:初步认识kotlin(第二篇)
上一章熟悉了kotlin基本的变量和函数声明,并明白了如何调用函数.本章再来看一些其他有用的东西 包括: 1. kotlin代码组织结构 2. when语法 3. 循环迭代语法 4. try表达式 1 ...
- Resource interpreted as Document but transferred with MIME type application/json laravel异常请求返回警告
一般情况下,laravel在方法里可以向前端返回数组格式 return [], 框架可以自动将数组转成JSON字符串返回,但浏览器会报MIME类型警告, 如是做APP接口可以忽视该警告: 但在前端aj ...
- JAVA构造函数在超类与子类定义鲁波总结
1.子类无构造函数,超类无构造函数,创建的无参数的对象: 编译通过. class A { } class B extends A { } public class Testeeer { public ...
- .NET自带IOC容器MEF之初体验(转)
本文主要把MEF作为一种IOC容器进行讲解,.net中可用的IOC容器非常多,如 CastleWindsor,Unity,Autofac,ObjectBuilder,StructureMap,Spri ...