C#多线程与UI响应 防止界面假死不响应(子线程创建的窗体获取消息响应用Application.DoEvent )

2023-05-16

一.
概述
在使用C#进行应用程序设计时,经常会采用多线程的方式进行一些后台任务的工作。对于不同的应用场景,使用的策略也不尽相同。
1.
后台循环任务,少量UI更新:例如批量上传文件,并提供进度。这种情况使用BackgroundWorker组件是非常好的选择。
2.
耗时的后台任务:这里的耗时任务是指一个时间较长的任务,并且不能精确获取进度,如:调用一个远程WebService接口。这种情况可以开两个线程,一个工作,一个更新UI(不能提供进度,只能显示动画表示系统在运行中)。
3.
耗时的UI任务:当工作压力集中在UI响应上时,可以在工作者线程中增加延时,从而让UI线程获得响应时间。整个工作的总体时间会增加,但用户响应效果会好很多。

二.
后台的循环任务,少量UI更新
这种情况使用BackgroundWorker组件是最好的选择。(详见附一)

三.
后台耗时任务
在后台执行一个不可分解的耗时任务,需要进行界面更新,以便让客户看上去程序有所响应。这种情况下,UI线程一般也不知道工作线程何时结束,所以一般执行循环任务,当工作线程结束后,关闭UI线程就可以了。

 

Thread uithread = null;

 

 


private void btnStart_Click(object sender, EventArgs e)

 

 


{

 

 


uithread = new Thread(new ThreadStart(this.UpdateProgressThread));

 

 


uithread.Start();

 

 

 

 

 


Thread workthread = new Thread(new ThreadStart(this.DoSomething));

 

 



workthread.Start();

 

 


}

 

 

 

 

 


private void DoSomething()

 

 


{

 

 


Thread.Sleep(5000);

 

 



uithread.Abort();

 

 


MessageBox.Show("work end");

 

 


}

 

 

 

 

 


private void UpdateProgressThread()

 

 


{

 

 


for (int i = 0; i < 10000; i++)

 

 


{

 

 


Thread.Sleep(100);

 

 


this.Invoke(new Action<int>(this.UpdateProgress), i);

 

 


}

 

 


}

 

 

 

 

 


private void UpdateProgress(int v)

 

 


{

 

 



this.progressBar1.Value = v;

 


}

这里只要注意一点:线程调用的方法都不能访问用户控件,必须通过委托调用Form的方法来实现界面更新。

四.
耗时的UI任务
当整个工作压力集中在UI响应上时,可以在工作者线程中增加延时,从而让UI线程获得响应时间。整个工作的总体时间会增加,但用户响应效果会好很多。

 

private void FormInitForm_Load(object sender, EventArgs e)

 

 


{

 

 


this.listView1.Items.Clear();

 

 


Thread workthread = new Thread(new ThreadStart(this.DoSomething));

 

 


workthread.Start();

 

 


}

 

 

 

 

 


private void DoSomething()

 

 


{

 

 


for (int i = 0; i < 30; i++)

 

 


{

 

 


this.Invoke(new Action<int>(this.LoadPicture), i);

 

 


Thread.Sleep(100);

 

 


}

 

 


}

 

 

 

 

 


private void LoadPicture(int i)

 

 


{

 

 


string text = string.Format("Item{0}", i);

 

 


ListViewItem lvi = new ListViewItem(text, 0);

 

 


this.listView1.Items.Add(lvi);

 

 


Thread.Sleep(200);//模拟耗时UI任务,非循环,不可分解

 


}

五.
补充
1.
Invoke 和 BeginInvoke
在多线程编程中,我们经常要在工作线程中去更新界面显示,而在多线程中直接调用界面控件的方法是错误的做法,正确的做法是将工作线程中涉及更新界面的代码封装为一个方法,通过 Invoke 或者 BeginInvoke 去调用,两者的区别就是一个导致工作线程等待,而另外一个则不会。
而所谓的“一面响应操作,一面添加节点”永远只能是相对的,使 UI 线程的负担不至于太大而以,因为界面的正确更新始终要通过 UI 线程去做,我们要做的事情是在工作线程中包揽大部分的运算,而将对纯粹的界面更新放到 UI 线程中去做,这样也就达到了减轻 UI 线程负担的目的了。

2.
Application.DoEvent
在耗时的循环的UI更新的方法中,插入Application.DoEvent,会使界面获得响应,Application.DoEvent会调用消息处理程序。

 

private void button2_Click(object sender, EventArgs e)

 

 


{

 

 


for (int i = 0; i < 30; i++)

 

 


{

 

 


string text = string.Format("Item{0}", i);

 

 


ListViewItem lvi = new ListViewItem(text, 0);

 

 


this.listView1.Items.Add(lvi);

 

 


Thread.Sleep(200);

 

 


for (int j = 0; j < 10; j++)

 

 


{

 

 


Thread.Sleep(10);

 

 


Application.DoEvents();

 

 


}

 

 


}

 


}
3.
Lock
lock(object)
{
}
等价与

 

try

 

 


{

 

 


Monitor.Enter(object);

 

 


}

 

 


finally

 

 


{

 

 


Monitor.Exit(object)

 


}

附一:

 

BackgroundWorker组件使用说明

 

一.
概述
BackgroundWorker是·NET 2.0提供的一个多线程组件,在应用程序中使用,可以非常简单方便地实现UI控件通信,并自动处理多线程冲突问题。

二.
基本属性

 

1.
WorkerReportsProgress ,bool:是否允许报告进度;

 

 

2.
WorkerSupportsCancellation,bool:是否允许取消线程。

 

 

3.
CancellationPending,bool,get:读取用户是否取消该线程。

 

 

 

 

三.
基本事件

 

1.
DoWork:工作者线程

 

 

2.
RunWorkerCompleted :线程进度报告

 

 

3.
ProgressChanged:线程结束报告

 


四.
基本方法

 

1.
RunWorkerAsync() :启动工作者线程;

 

 

2.
CancelAsync():取消工作者线程;

 

 

3.
ReportProgress(int);
报告进度

 


五.
代码

 

//启动

 

 

private void btnStart_Click(object sender, EventArgs e)

 

 

{

 

 


this.btnStart.Enabled = false;

 

 


this.btnStop.Enabled = true;

 

 

 

 

 


this.backgroundWorker.RunWorkerAsync();

 

 

}

 

 

 

 

 

//通知线程停止

 

 

private void btnStop_Click(object sender, EventArgs e)

 

 

{

 

 


this.backgroundWorker.CancelAsync();

 

 

}

 

 

 

 

 

//工作者线程

 

 

private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)

 

 

{

 

 


for (int i = 0; i < 150; i++)

 

 



{

 

 


if (backgroundWorker.CancellationPending)
//查看用户是否取消该线程

 

 


{

 

 


break;

 

 


}

 

 

 

 

 


System.Threading.Thread.Sleep(50);
//干点实际的事

 

 

 

 

 


backgroundWorker.ReportProgress(i);
//报告进度

 

 


}

 

 

}

 

 

 

 

 

//线程进度报告

 

 

private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)

 

 

{

 

 


this.progressBar1.Value = e.ProgressPercentage * 100 / 150;

 

 

}

 

 

 

 

 

//线程结束报告

 

 

private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

 

 

{

 

 


this.btnStart.Enabled = true;

 

 


this.btnStop.Enabled = false;

 

}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C#多线程与UI响应 防止界面假死不响应(子线程创建的窗体获取消息响应用Application.DoEvent ) 的相关文章

  • 多核编程与单核多线程编程

    并发 xff1a 时间段内有很多的线程或进程在执行 xff0c 但何时间点上都只有一个在执行 xff0c 多个线程或进程争抢时间片轮流执行 并行 xff1a 时间段和时间点上都有多个线程或进程在执行 单核cpu的话只能是并发 xff0c 多
  • 多线程与单核cpu,多核cpu概念

    1 多线程在单核和多核CPU上的执行效率问题的讨论 a1 多线程在单cpu中其实也是顺序执行的 xff0c 不过系统可以帮你切换那个执行而已 xff0c 其实并没有快 xff08 反而慢 xff09 多个cpu的话就可以在两个cpu中同时执
  • 多CPU,多核,多进程,多线程以及进程和线程的简单理解以及区别

    当面临这些问题的时候 xff0c 有两个关键词无法绕开 xff0c 那就是并行和并发 首先 xff0c 要先了解几个概念 xff1a 1 进程是程序的一次执行 2 进程是资源分配的基本单位 3 一个进程可以包括多个线程 4 在单CPU计算机
  • 并行、并发,两者的区别

    并发和并行相似但又是两个不同的概念 xff0c 并行是指两个或者多个时间在同一时刻发生 xff0c 就好比如多个程序同时运行 而并发是指两个或者多个时间在同一时间间隔内发生 在多道程序环境下 xff0c 并发性是指在一段时间内宏观上有多个程
  • Ubuntu VNC安装与遇到的问题

    Linux下VNC server可选的很多 xff0c vnc4server tightvnc xff0c 我更喜欢tigervnc unity桌面环境在vnc中不能正常使用 xff0c 我翻遍互联网还是没找到很好的解决办法 xff0c 退
  • CPU的主频,总线频率和L2缓存对电脑的性能有哪些影响

    CPU是Central Processing Unit的缩写 xff0c 即中央处理器 CPU发展至今 xff0c 其中所集成的电子元件也越来越多 xff0c 上万个晶体管构成了CPU的内部结构 那么这上百万个晶体管是如何工作的呢 xff1
  • 寄存器(cpu工作原理)

    来源 xff1a 王爽老师的 汇编语言 1 一个典型的CPU由运算器 控制器 寄存器等器件组成 xff0c 这些器件靠内部总线相连 区别 xff1a 内部总线实现CPU内部各个器件之间的联系 外部总线实现CPU和主板上其它器件的联系 808
  • 寄存器、存储器、内存的区别

    从范围来看 xff0c 它们所指的范畴不一样 寄存器是中央处理器内的组成部份 它跟CPU有关 寄存器是有限存贮容量的高速存贮部件 xff0c 它们可用来暂存指令 数据和位址 在中央处理器的控制部件中 xff0c 包含的寄存器有指令寄存器 I
  • 寄存器(CPU的工作原理)

    通用寄存器 8086CPU有14个寄存器 xff0c 他们的名字称为诶 xff1a AX BX CX DX SI DI SP BP IP CS SS DS ES PSW 8086CPU所有的寄存器都是16位的 xff0c 可以存放两个字节
  • CPU,寄存器,内存三者的关系

    一 皇帝身边的小太监 寄存器 不知道什么是寄存器 xff1f 那见过太监没有 xff1f 没有 xff1f 其实我也没有 没见过不要紧 xff0c 见过就麻烦大了 xff0c 大家都看过古装戏 xff0c 那些皇帝们要阅读奏章的时候 xff
  • 内网和外网通信的问题

    各位大神 xff0c 我把两台电脑 xff0c 其中一台通过路由器映射了 xff0c 也就成为了所为的服务器了 xff0c 另一台也就是处在内网的 xff0c 也就是我们平时一般人的电脑 xff0c 也就是客户端了 xff0c 那么客户端是
  • OpenCV进行图像相似度对比的几种办法

    转载请注明出处 xff1a http blog csdn net wangyaninglm article details 43853435 xff0c 来自 xff1a shiter编写程序的艺术 对计算图像相似度的方法 xff0c 本文
  • Python+Opencv识别两张相似图片

    在网上看到python做图像识别的相关文章后 xff0c 真心感觉python的功能实在太强大 xff0c 因此将这些文章总结一下 xff0c 建立一下自己的知识体系 当然了 xff0c 图像识别这个话题作为计算机科学的一个分支 xff0c
  • OpenCV进行图像相似度对比的几种办法

    1 直方图方法 方法描述 xff1a 有两幅图像patch 当然也可是整幅图像 xff0c 分别计算两幅图像的直方图 xff0c 并将直方图进行归一化 xff0c 然后按照某种距离度量的标准进行相似度的测量 方法的思想 xff1a 基于简单
  • 比较两幅图像的相似度

    现在以图搜图的功能比较火热 xff0c 很好奇其原理 简单的搜索学习得知 xff0c 实现相似图片搜索的关键技术是 感知哈希算法 xff0c 作用是对每一张图片按照某种规律生成一个对应的指纹字符串 比较不同图片之间的指纹字符串 xff0c
  • 查找文献方法整理

    1 dblp computer science bibliography 在搜索框查找文献关键字 xff0c 或文献标题 xff0c 右边可以筛选 找到想找的论文之后 xff0c 可以将鼠标移到view xff0c 找到原网站 有些网站可以
  • OpenCV进行图像相似度对比的几种办法

    平均哈希算法 实现步骤 缩小尺寸 xff1a 将图像缩小到8 8的尺寸 xff0c 总共64个像素 这一步的作用是去除图像的细节 xff0c 只保留结构 明暗等基本信息 xff0c 摒弃不同尺寸 比例带来的图像差异 xff1b 简化色彩 x
  • opencv学习笔记(六)直方图比较图片相似度

    opencv学习笔记 xff08 六 xff09 直方图比较图片相似度 opencv提供了API来比较图片的相似程度 xff0c 使我们很简单的就能对2个图片进行比较 xff0c 这就是直方图的比较 xff0c 直方图英文是histogra
  • 完整opencv(emgucv)人脸、检测、采集、识别、匹配、对比

    成对几何直方图匹配 public static string MatchHist string haarXmlPath 61 64 34 haarcascade frontalface alt tree xml 34 HaarCascade
  • OpenCV学习笔记——判断两张图的相似度

    判断两张图的相似度 方法 直方图对比法ORB算法 实验 1 直方图对比法 参考如何使用OpenCV3直方图方法进行人脸相似度对比 因为我的环境是VS2010 43 OpenCV2 4 8 xff0c 所以在原版的基础上做了一点小修改 inc

随机推荐

  • 【OpenCV】直方图应用:直方图均衡化,直方图匹配,对比直方图

    本文链接 xff1a https blog csdn net xiaowei cqu article details 7606607 前面介绍了 数字图像灰度直方图 xff0c 现在来尝试直方图的应用 直方图均衡化 直方图均衡化 xff08
  • C# Socket服务器及多客户端连接应用例程

    服务端代码 控制台示例 static List lt Socket gt Sockets 61 new List lt Socket gt static void Main string args int port 61 10 byte b
  • C#Socket文件传输(发送与接收代码)

    原文链接 xff1a http www cnblogs com reynoldchan p 3762014 html 这里是发送的代码 xff1a SendVarData是转码后发送函数 1 lt summary gt 2 发送文件 3 l
  • C# Socket服务端与客户端通信(包含大文件的断点传输)

    步骤 xff1a 一 服务端的建立 1 服务端的项目建立以及页面布局 2 各功能按键的事件代码 1 xff09 传输类型说明以及全局变量 2 xff09 Socket通信服务端具体步骤 xff1a xff08 1 xff09 建立一个Soc
  • C# Socket案例(服务端与客户端)

    本文链接 xff1a https blog csdn net qq 42203978 article details 80520299 服务端完整代码 using System using System Net using System N
  • C#中的Socket编程-TCP客户端

    TCP客户端 using System using System Collections Generic using System Linq using System Net using System Net Sockets using S
  • 最小花费(图论算法)

    Description 在n个人中 xff0c 某些人的银行账号之间可以互相转账 这些人之间转账的手续费各不相同 给定这些人之间转账时需要从转账金额里扣除百分之几的手续费 xff0c 请问A最少需要多少钱使得转账后B收到100元 Input
  • C#获取本机IP地址字符串

    1 using System Net 2 using System Net Sockets 3 4 lt summary gt 5 获取本机IP地址 6 lt summary gt 7 lt returns gt 本机IP地址 lt ret
  • C#中Socket通信用法实例详解

    本文实例讲述了C 中Socket通信用法 分享给大家供大家参考 具体如下 xff1a 一 UDP方式 xff1a 服务器端代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
  • 内存溢出问题分析

    System OutOfMemoryException 大多是数据处理的过程中 xff0c 对内存资源的管控太过于粗放 建议对于非托管资源的使用不要过于随意 内存分配的时候使用内存池的模式 xff0c 避免内存泄露和内存碎片 你的指针可能成
  • C# GC 垃圾回收机制原理

    转载参照自以下文章 xff1a http www cnblogs com fdyang p 3456258 html c 销毁资源和释放内存 https www cnblogs com Jessy articles 2552839 html
  • c++中 结构体和类的区别

    区别 xff1a 结构是一种用关键字struct声明的自定义数据类型 与类相似 xff0c 也可以包含构造函数 xff0c 常数 xff0c 字段 xff0c 方法 xff0c 属性 xff0c 索引器 xff0c 运算符和嵌套类型等 xf
  • 结构体和类的区别

    结构体和类的区别 xff1a 在做一个项目时 xff0c 使用了较多的结构体 xff0c 并且存在一些结构体的嵌套 xff0c 即某结构体成员集合包含另一个结构体等 xff0c 总是出现一些奇怪的错误 xff0c 才终于下决心好好分析一下到
  • C++程序内存分配方式(堆与栈)

    一 内存布局 1 栈区 xff08 stack xff09 xff1a 由编译器自动分配释放 xff0c 存放函数的参数值 xff0c 局部变量值等 xff0c 其操作方法类似数据结构中的栈 2 堆区 xff08 heap xff09 xf
  • 值引用和引用问题分析

    在c 43 43 中 xff0c 声明一个全局变量A a xff0c 然后在一个局部函数中创建类型A c赋值给a xff0c 对于值类型 xff0c 如结构体等 xff0c 是在栈中分配内存c xff0c 然后拷贝其内存所有值给a xff0
  • vs中寄存器调试窗口可看出程序是多少位运行的及cpu寄存器使用情况

    如果不清楚程序是多少位运行的 xff0c 可在vs中的调试 gt 寄存器调试窗口查看寄存器的寻址位数 xff0c 如果是32位的exe则寄存器寻址显示32位 xff0c 64位的exe则显示64位寻址 1 根据IntPtr Size来判断
  • CentOS6 Squid代理服务器的安装与配置

    一 简介 代理服务器英文全称是Proxy Server xff0c 其功能就是代理网络用户去取得网络信息 Squid是一个缓存Internet 数据的软件 xff0c 其接收用户的下载申请 xff0c 并自动处理所下载的数据 当一个用户想要
  • c++中类型用new和不用new的区别

    解析一 xff1a new创建类对象 xff0c 使用完后需使用delete删除 xff0c 跟申请内存类似 所以 xff0c new有时候又不太适合 xff0c 比如在频繁调用场合 xff0c 使用局部new类对象就不是个好选择 xff0
  • C# Thread开启线程几种方式

    using System using System Collections Generic using System Linq using System Text using System Threading using System Th
  • C#多线程与UI响应 防止界面假死不响应(子线程创建的窗体获取消息响应用Application.DoEvent )

    一 xff0e 概述 在使用C 进行应用程序设计时 xff0c 经常会采用多线程的方式进行一些后台任务的工作 对于不同的应用场景 xff0c 使用的策略也不尽相同 1 后台循环任务 xff0c 少量UI更新 xff1a 例如批量上传文件 x