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

跨线程操作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#跨线程操作控件的最简单实现探究的更多相关文章

  1. WinForm中新开一个线程操作 窗体上的控件(跨线程操作控件)

    最近在做一个winform的小软件(抢票的...).登录窗体要从远程web页面获取一些数据,为了不阻塞登录窗体的显示,开了一个线程去加载数据远程的数据,会报一个错误"线程间操作无效: 从不是 ...

  2. C# 跨线程操作控件(简洁)

                                              C# 跨线程操作控件 .net 原则上禁止跨线程访问控件,因为这样可能造成错误的发生.解决此问题的方法有两个: 第一 ...

  3. winform 跨线程操作控件

    当进行winform的开发时,经常遇到用时比较久的操作,在传统的单线程程序中,用户必须等待这个耗时操作完成以后才能进行下一步的操作,这个时候,多线程编程就派上用场了,将这个耗时的操作放到一个新的子线程 ...

  4. C#跨线程操作控件

    1.首先通过按键创建子线程: 创建子线程,子线程调用changeText方法. private void btnOK_Click(object sender, EventArgs e) { Threa ...

  5. 在.Net中进行跨线程的控件操作(下篇:BackgroundWorker)

    在.Net中,如果我们在非UI线程上访问窗体上的控件的时候,会产生一个跨线程调用的异常,那么如何处理这种情况呢?在上一章中,我介绍了使用Control.Invoke方法,如果你不习惯使用委托,那么.N ...

  6. C# 跨线程调用控件

    在C# 的应用程序开发中, 我们经常要把UI线程和工作线程分开,防止界面停止响应.  同时我们又需要在工作线程中更新UI界面上的控件, 下面介绍几种常用的方法 阅读目录 线程间操作无效 第一种办法:禁 ...

  7. winform跨线程访问控件

    首先说下,.net 2.0以后加强了安全机制,不允许在winform中直接跨线程访问控件的属性.所以除了控件所在的线程外的线程调用会抛异常 (Cross-thread operation not va ...

  8. C# 关于跨线程访问控件问题

    跨线程访问控件问题的原因是:控件都是在主线程中创建的,而系统默认控件的修改权归其创建线程所有.在子线程中如果需要直接修改控件的内容,需要使用委托机制将控件的修改操作交给主线程处理.因此,当没有使用委托 ...

  9. 【转载】C# 跨线程调用控件

    转自:http://www.cnblogs.com/TankXiao/p/3348292.html 感谢原作者,转载以备后用 在C# 的应用程序开发中, 我们经常要把UI线程和工作线程分开,防止界面停 ...

随机推荐

  1. IMAP简单研究

    IMAP的相关详细介绍: http://www.imapwiki.org/ClientImplementationhttp://tools.ietf.org/html/rfc3501 1.连接服务器 ...

  2. REST API权限集成设计

    REST API权限集成设计 应用分为两大部分,前端html+后端Rest服务,前端html和后端Rest服务部署完全分离. 目标:可访问资源都处于权限控制之下(意味着通过浏览器地址栏的任意url都会 ...

  3. Unit03: 容器对路径的处理 、 Servlet特性

    Unit03: 容器对路径的处理 . Servlet特性 案例一:查询,增加员工: 重定向 处理请求资源路径 目录结构: 案例代码: package dao; import java.io.Seria ...

  4. dubbox实现REST服务

    一.dubbox的由来 dubbox是当当网基于dubbo的基础上开发的扩展版,也可以认为是dubbo的升级版,根据当前互联网的应用需求,增加了很多扩展的功能. dubbox并没有发布到maven中央 ...

  5. 十八 线程暂停 suspend/ resume

    1  Suspend.resume 的缺点1 :独占!  线程执行到同步块中,如果线程暂停了,不会释放锁. 比如,比如System.out.println()方法就是一个同步方法, 如果线程调用Sys ...

  6. MyBatis的适用场景和生命周期

    MyBatis使用场景 对比Hibernate和MyBatis是我们常见的话题,Hibernate作为常用的ORM框架,它使用起来简单易懂,对于SQL语言的封装,让对于SQL并不是很熟练的程序员也可以 ...

  7. 反向生成hibernate实体类和映射文件

    工欲善其事,必先利其器.我们可以使用IDE来根据数据库中的表反向生成实体类和映射文件,虽然这些东西手写也并不是难度很大,但是如果存在大量的简单工作需要我们做,也会显得很麻烦. 写在前面 我们反向生成的 ...

  8. Java类的初始化与实例对象的初始化

    Java对象初始化详解 2013/04/10 · 开发 · 1 评论· java 分享到:43 与<YII框架>不得不说的故事—扩展篇 sass进阶篇 Spring事务管理 Android ...

  9. spring-boot-maven-plugin 插件的作用

    pom文件中添加了"org.springframework.boot:spring-boot-maven-plugin"插件.在添加了该插件之后,当运行"mvn pack ...

  10. MySQL修改redo_log_size

    MySQL5.5 步骤如下: 1. set global innodb_fast_shutdown = 0; 2. mysqladmin shutdown 3. 修改my.cnf innodb_log ...