c#中多线程访问winform控件的若干问题小结
发布时间:2023-09-23 17:04:27
我们在做winform应用的时候,大部分情况下都会碰到使用多线程控制界面上控件信息的问题。然而我们并不能用传统方法来解决这个问题,下面我将详细的介绍。
首先来看传统方法:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Thread thread = new Thread(ThreadFuntion);
thread.IsBackground = true;
thread.Start();
}
private void ThreadFuntion()
{
while (true)
{
this.textBox1.Text = DateTime.Now.ToString();
Thread.Sleep(1000);
}
}
}
运行这段代码,我们会看到系统抛出一个异常:Cross-thread operation not valid:Control 'textBox1' accessed from a thread other than the thread it was created on . 这是因为.net 2.0以后加强了安全机制,不允许在winform中直接跨线程访问控件的属性。那么怎么解决这个问题呢,下面提供几种方案。
第一种方案,我们在Form1_Load()方法中加一句代码:
private void Form1_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;
Thread thread = new Thread(ThreadFuntion);
thread.IsBackground = true;
thread.Start();
}
加入这句代码以后发现程序可以正常运行了。这句代码就是说在这个类中我们不检查跨线程的调用是否合法(如果没有 加这句话运行也没有异常,那么说明系统以及默认的采用了不检查的方式)。然而,这种方法不可取。我们查看CheckForIllegalCrossThreadCalls 这个属性的定义,就会发现它是一个static的,也就是说无论我们在项目的什么地方修改了这个值,他就会在全局起作用。而且像这种跨线程访问是否存在异常,我们通常都会去检查。如果项目中其他人修改了这个属性,那么我们的方案就失败了,我们要采取另外的方案。
下面来看第二种方案,就是使用delegate和invoke来从其他线程中控制控件信息。网上有很多人写了这种控制方式,然而我看了很多这种帖子,表明上看来是没有什么问题的,但是实际上并没有解决这个问题,首先来看网络上的那种不完善的方式:
public partial class Form1 : Form
{
private delegate void FlushClient();//代理
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Thread thread = new Thread(CrossThreadFlush);
thread.IsBackground=true;
thread.Start();
}
private void CrossThreadFlush()
{
//将代理绑定到方法
FlushClient fc = new FlushClient(ThreadFuntion);
this.BeginInvoke(fc);//调用代理
}
private void ThreadFuntion()
{
while (true)
{
this.textBox1.Text = DateTime.Now.ToString();
Thread.Sleep(1000);
}
}
}
使用这种方式我们可以看到跨线程访问的异常没有了。但是新问题出现了,界面没有响应了。为什么会出现这个问题, 我们只是让新开的线程无限循环刷新,理论上应该不会对主线程产生影响的。其实不然,这种方式其实相当于把这个新开的线程“注入”到了主控制线程中,它取得了主线程的控制。只要这个线程不返回,那么主线程将永远都无法响应。就算新开的线程中不使用无限循环,使可以返回了。这种方式的使用多线程也失去了它本来的意义。
现在来让我们看看推荐的解决方案:
public partial class Form1 : Form
{
private delegate void FlushClient();//代理
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Thread thread = new Thread(CrossThreadFlush);
thread.IsBackground = true;
thread.Start();
}
private void CrossThreadFlush()
{
while (true)
{
//将sleep和无限循环放在等待异步的外面
Thread.Sleep(1000);
ThreadFunction();
}
}
private void ThreadFunction()
{
if (this.textBox1.InvokeRequired)//等待异步
{
FlushClient fc = new FlushClient(ThreadFunction);
this.Invoke(fc);//通过代理调用刷新方法
}
else
{
this.textBox1.Text = DateTime.Now.ToString();
}
}
}
运行上述代码,我们可以看到问题已经被解决了,通过等待异步,我们就不会总是持有主线程的控制,这样就可以再不发生跨线程调用异常的情况下完成多线程对winform多线程控件的控制了。
对于深山老林提出的问题,我最近找到了更优的解决方案,利用了delegate的异步调用,大家可以看看:
public partial class Form1 : Form
{
private delegate void FlushClient();//代理
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Thread thread = new Thread(CrossThreadFlush);
thread.IsBackground = true;
thread.Start();
}
private void CrossThreadFlush()
{
FlushClient fc=new FlushClient(ThreadFunction);
fc.BeginInvoke(null,null);
}
private void ThreadFunction()
{
while (true)
{
this.textBox1.Text = DateTime.Now.ToString();
Thread.Sleep(1000);
}
}
}
这种方法也可以直接简化为(因为delegate的异步就是开了一个异步线程):
public partial class Form1 : Form
{
private delegate void FlushClient();//代理
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
FlushClient fc=new FlushClient(ThreadFunction);
fc.BeginInvoke(null,null);
}
private void ThreadFunction()
{
while (true)
{
this.textBox1.Text = DateTime.Now.ToString();
Thread.Sleep(1000);
}
}
}
贫僧用飘柔反馈
while (true)和Thread.Sleep(1000);不要放在invoke里面去控制就行了


猜你喜欢
- 本文实例为大家解析了Zxing生成二维码的经典案例,供大家参考,具体内容如下1、首先呢,先编译 compile ‘com.google.zx
- 前言在开发中,或多或少会使用唤醒锁(wake lock),有的是为了保持屏幕长亮,有的是为了保持 CPU 运行。唤醒锁的本质,其实是对屏幕状
- 在实践中,项目的某些配置信息是需要进行加密处理的,以减少敏感信息泄露的风险。比如,在使用Druid时,就可以基于它提供的公私钥加密方式对数据
- 前言在 Java 中,跳转的实现方式有两种:请求转发和请求重定向,但二者是完全不同的,所以我们今天就来盘它。请求转发和请求重定向主要区别,包
- MainActivity如下: package cn.testjavascript; import java.util.StringToke
- 一,使用背景之前遇到一个需求,是需要将一个json文件解析存储到数据库中。一开始测试的时候,json文件的大小都在几兆以内,所以直接将jso
- 问题描述如果直接获取某个json数组中的元素将得到如下的json{ "44": { "height
- 在Java中进行日期处理大家一定都不陌生,我们经常会需要在代码中进行日期的转换、日期的格式化等操作。而一般我们进行日期格式化的时候都会使用S
- 一、Flutter介绍Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。Flutter可以与现
- 前言定时/计划功能在Java应用的各个领域都使用得非常多,比方说Web层面,可能一个项目要定时采集话单、定时更新某些缓存、定时清理一批不活跃
- 我们知道,spring boot自动配置功能可以根据不同情况来决定spring配置应该用哪个,不应该用哪个,举个例子:Spring的Jdbc
- 前言无论Spring的XML配置或者Java配置,都可以配置自动扫描,也就是在指定包及其子包下的component,都会被自动扫描并被Spr
- 由于最近项目忙,博客一直没有时间更新,今天有点时间就连续更新两篇吧,过过瘾。这篇图片拖拽缩放也是我在项目中用到的,今天整理一下,将源码奉献给
- Java二叉树排序算法排序二叉树的描述也是一个递归的描述, 所以排序二叉树的构造自然也用递归的:排序二叉树的3个特征:1:当前node的所有
- 首先来看看Map集合获取元素的三种常见方法keySet()、values()、entrySet()1. values():返回map集合的所
- 一、简介Mutex的突出特点是可以跨应用程序域边界对资源进行独占访问,即可以用于同步不同进程中的线程,这种功能当然这是以牺牲更多的系统资源为
- 前言:mybatisplus 可以说是对mybatis更好的拓展,一些简单的增删改查的操作已经被作者实现,我们只需引用即可。1.数据库建表这
- Strut2判断是否是AJAX调用1. AJAX与传统Form表单实际上,两者一般都是通过HTTP的POST请求。区
- 本文实例讲述了java图片滑动验证(登录验证)原理与实现方法。分享给大家供大家参考,具体如下:这是我简单做出的效果图,处理300X150px
- 话不多说,上代码: /** * 获取字符串的长度,如果有中文,则每个中文字符计为2位 * @param value