浅谈C#多线程与UI响应
www.educity.cn 发布者:shenywww 来源:网络转载 发布日期:2014年10月06日 文章评论 发表文章
一.C#多线程概述
1.后台循环任务,少量UI更新:例如批量上传文件,并提供进度。这种情况使用BackgroundWorker组件是非常好的选择。
2.耗时的后台任务:这里的耗时任务是指一个时间较长的任务,并且不能精确获取进度,如:调用一个远程WebService接口。这种情况可以开两个线程,一个工作,一个更新UI(不能提供进度,只能显示动画表示系统在运行中)。www.educity.cn 发布者:shenywww 来源:网络转载 发布日期:2014年10月06日 文章评论 发表文章
一.C#多线程概述
1.后台循环任务,少量UI更新:例如批量上传文件,并提供进度。这种情况使用BackgroundWorker组件是非常好的选择。
2.耗时的后台任务:这里的耗时任务是指一个时间较长的任务,并且不能精确获取进度,如:调用一个远程WebService接口。这种情况可以开两个线程,一个工作,一个更新UI(不能提供进度,只能显示动画表示系统在运行中)。
3.耗时的UI任务:当工作压力集中在UI响应上时,可以在工作者线程中增加延时,从而让UI线程获得响应时间。整个工作的总体时间会增加,但用户响应效果会好很多。
二.后台的循环任务,少量UI更新
这种情况使用BackgroundWorker组件是最好的选择。(详见附一)
三.后台耗时任务
在后台执行一个不可分解的耗时任务,需要进行界面更新,以便让客户看上去程序有所响应。这种情况下,UI线程一般也不知道工作线程何时结束,所以一般执行循环任务,当工作线程结束后,关闭UI线程就可以了。
Threaduithread=null; privatevoidbtnStart_Click(objectsender,EventArgse) { uithread=newThread(newThreadStart(this.UpdateProgressThread)); uithread.Start(); Threadworkthread=newThread(newThreadStart(this.DoSomething)); workthread.Start(); } privatevoidDoSomething() { Thread.Sleep(5000); uithread.Abort(); MessageBox.Show("workend"); } privatevoidUpdateProgressThread() { for(inti=0;i<10000;i++) { Thread.Sleep(100); this.Invoke(newAction(this.UpdateProgress),i); } } privatevoidUpdateProgress(intv) { this.progressBar1.Value=v; }
这里只要注意一点:线程调用的方法都不能访问用户控件,必须通过委托调用Form的方法来实现界面更新。
四.耗时的UI任务
当整个工作压力集中在UI响应上时,可以在工作者线程中增加延时,从而让UI线程获得响应时间。整个工作的总体时间会增加,但用户响应效果会好很多。
privatevoidFormInitForm_Load(objectsender,EventArgse) { this.listView1.Items.Clear(); Threadworkthread=newThread(newThreadStart(this.DoSomething)); workthread.Start(); } privatevoidDoSomething() { for(inti=0;i<30;i++) { this.Invoke(newAction(this.LoadPicture),i); Thread.Sleep(100); } } privatevoidLoadPicture(inti) { stringstringtext=string.Format("Item{0}",i); ListViewItemlvi=newListViewItem(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会调用消息处理程序。
privatevoidbutton2_Click(objectsender,EventArgse) { for(inti=0;i<30;i++) { stringstringtext=string.Format("Item{0}",i); ListViewItemlvi=newListViewItem(text,0); this.listView1.Items.Add(lvi); Thread.Sleep(200); for(intj=0;j<10;j++) { Thread.Sleep(10); Application.DoEvents(); } } } 3.Lock lock(object) { } 等价与 try { Monitor.Enter(object); } finally { Monitor.Exit(object) }
附一:
BackgroundWorker组件使用说明
一.概述
BackgroundWorker是·NET2.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);报告进度
五.代码
//启动 privatevoidbtnStart_Click(objectsender,EventArgse) { this.btnStart.Enabled=false; this.btnStop.Enabled=true; this.backgroundWorker.RunWorkerAsync(); } //通知线程停止 privatevoidbtnStop_Click(objectsender,EventArgse) { this.backgroundWorker.CancelAsync(); } //工作者线程 privatevoidbackgroundWorker_DoWork(objectsender,DoWorkEventArgse) { for(inti=0;i<150;i++) { if(backgroundWorker.CancellationPending)//查看用户是否取消该线程 { break; } System.Threading.Thread.Sleep(50);//干点实际的事 backgroundWorker.ReportProgress(i);//报告进度 } } //线程进度报告 privatevoidbackgroundWorker_ProgressChanged(objectsender,ProgressChangedEventArgse) { this.progressBar1.Value=e.ProgressPercentage*100/150; } //线程结束报告 privatevoidbackgroundWorker_RunWorkerCompleted(objectsender,RunWorkerCompletedEventArgse) { this.btnStart.Enabled=true; this.btnStop.Enabled=false; }
3.耗时的UI任务:当工作压力集中在UI响应上时,可以在工作者线程中增加延时,从而让UI线程获得响应时间。整个工作的总体时间会增加,但用户响应效果会好很多。
二.后台的循环任务,少量UI更新
这种情况使用BackgroundWorker组件是最好的选择。(详见附一)
三.后台耗时任务
在后台执行一个不可分解的耗时任务,需要进行界面更新,以便让客户看上去程序有所响应。这种情况下,UI线程一般也不知道工作线程何时结束,所以一般执行循环任务,当工作线程结束后,关闭UI线程就可以了。
Threaduithread=null; privatevoidbtnStart_Click(objectsender,EventArgse) { uithread=newThread(newThreadStart(this.UpdateProgressThread)); uithread.Start(); Threadworkthread=newThread(newThreadStart(this.DoSomething)); workthread.Start(); } privatevoidDoSomething() { Thread.Sleep(5000); uithread.Abort(); MessageBox.Show("workend"); } privatevoidUpdateProgressThread() { for(inti=0;i<10000;i++) { Thread.Sleep(100); this.Invoke(newAction(this.UpdateProgress),i); } } privatevoidUpdateProgress(intv) { this.progressBar1.Value=v; }
这里只要注意一点:线程调用的方法都不能访问用户控件,必须通过委托调用Form的方法来实现界面更新。
四.耗时的UI任务
当整个工作压力集中在UI响应上时,可以在工作者线程中增加延时,从而让UI线程获得响应时间。整个工作的总体时间会增加,但用户响应效果会好很多。
privatevoidFormInitForm_Load(objectsender,EventArgse) { this.listView1.Items.Clear(); Threadworkthread=newThread(newThreadStart(this.DoSomething)); workthread.Start(); } privatevoidDoSomething() { for(inti=0;i<30;i++) { this.Invoke(newAction(this.LoadPicture),i); Thread.Sleep(100); } } privatevoidLoadPicture(inti) { stringstringtext=string.Format("Item{0}",i); ListViewItemlvi=newListViewItem(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会调用消息处理程序。
privatevoidbutton2_Click(objectsender,EventArgse) { for(inti=0;i<30;i++) { stringstringtext=string.Format("Item{0}",i); ListViewItemlvi=newListViewItem(text,0); this.listView1.Items.Add(lvi); Thread.Sleep(200); for(intj=0;j<10;j++) { Thread.Sleep(10); Application.DoEvents(); } } } 3.Lock lock(object) { } 等价与 try { Monitor.Enter(object); } finally { Monitor.Exit(object) }
附一:
BackgroundWorker组件使用说明
一.概述
BackgroundWorker是·NET2.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);报告进度
五.代码
//启动 privatevoidbtnStart_Click(objectsender,EventArgse) { this.btnStart.Enabled=false; this.btnStop.Enabled=true; this.backgroundWorker.RunWorkerAsync(); } //通知线程停止 privatevoidbtnStop_Click(objectsender,EventArgse) { this.backgroundWorker.CancelAsync(); } //工作者线程 privatevoidbackgroundWorker_DoWork(objectsender,DoWorkEventArgse) { for(inti=0;i<150;i++) { if(backgroundWorker.CancellationPending)//查看用户是否取消该线程 { break; } System.Threading.Thread.Sleep(50);//干点实际的事 backgroundWorker.ReportProgress(i);//报告进度 } } //线程进度报告 privatevoidbackgroundWorker_ProgressChanged(objectsender,ProgressChangedEventArgse) { this.progressBar1.Value=e.ProgressPercentage*100/150; } //线程结束报告 privatevoidbackgroundWorker_RunWorkerCompleted(objectsender,RunWorkerCompletedEventArgse) { this.btnStart.Enabled=true; this.btnStop.Enabled=false; }
浅谈C#多线程与UI响应的更多相关文章
- 浅谈iOS多线程
浅谈iOS多线程 首先,先看看进程和线程的概念. 图1.1 这一块不难理解,重点点下他们的几个重要区别: 1,地址空间和资源:进程可以申请和拥有系统资源,线程不行.资源进程间相互独立,同一进程的各线程 ...
- C#多线程与UI响应 跨线程更新UI
最近在写一个TCP通信程序,自定义了一个通信类TCPclient,用于客户端异步接收和发送网络消息. TCPclient中定义了一个接收到新的网络消息事件: //收到新消息事件 public dele ...
- 转载:浅谈Java多线程的同步问题【很好我就留下来,多分共享】
转载:http://www.cnblogs.com/phinecos/archive/2010/03/13/1684877.html#undefined 多线程的同步依靠的是对象锁机制,synchro ...
- 浅谈Java多线程的同步问题 【转】
多线程的同步依靠的是对象锁机制,synchronized关键字的背后就是利用了封锁来实现对共享资源的互斥访问. 下面以一个简单的实例来进行对比分析.实例要完成的工作非常简单,就是创建10个线程,每个线 ...
- 浅谈Java多线程同步机制之同步块(方法)——synchronized
在多线程访问的时候,同一时刻只能有一个线程能够用 synchronized 修饰的方法或者代码块,解决了资源共享.下面代码示意三个窗口购5张火车票: package com.jikexueyuan.t ...
- 浅谈Java多线程
线程与进程 什么是进程? 当一个程序进入内存中运行起来它就变为一个进程.因此,进程就是一个处于运行状态的程序.同时进程具有独立功能,进程是操作系统进行资源分配和调度的独立单位. 什么是线程? 线程是进 ...
- 浅谈 Java 多线程(一) --- JMM
为什么使用多线程 更多的处理器核心数(硬件的发展使 CPU 趋向于更多的核心数,如果不能充分利用,就无法显著提升程序的效率) 更快的响应时间(复杂的业务场景下,会存在许多数据一致性不强的操作,如果将这 ...
- 浅谈Java多线程中的join方法
先上代码 新建一个Thread,代码如下: package com.thread.test; public class MyThread extends Thread { private String ...
- 【转】浅谈多核CPU、多线程、多进程
浅谈多核CPU.多线程.多进程 1.CPU发展趋势 核心数目依旧会越来越多,依据摩尔定律,由于单个核心性能提升有着严重的瓶颈问题,普通的桌面PC有望在2017年末2018年初达到24核心(或者16核3 ...
随机推荐
- 【EasyNetQ】- 发送接收
发布/订阅和请求/响应模式是位置透明的,因为您不需要指定消息的使用者所在的位置,而发送/接收模式专门用于通过命名队列进行通信.它也不会假设可以通过队列发送的消息类型.这意味着您可以通过同一队列发送不同 ...
- perf 对两个map是否重叠的判断,以及函数map_groups__fixup_overlappings代码逻辑
该标题可以抽象出来的问题是:两个前开后闭的区间 rangeA 和 rangeB,如何判断这两个区间是否重叠.这个问题在内核中非常重要,虚拟地址空间的划分需要它,perf中map_group的构建也需要 ...
- ps学习笔记(二)
1)选择所有图层: Ctrl+Alt+A2)查找层:ctrl+alt+shift+f,需要在层面板输入查找层名,可自动查找层:3)隔离层:可将选择图层,更改为隔离,只对选择的层编辑:注:图层面板中有一 ...
- 隐马尔可夫模型HMM
隐马尔可夫模型HMM的探究 1 HMM基本概念1.1 定义1.2 观测序列生成过程1.3 HMM的三个问题2 概率计算算法2.1 直接计算算法2.2 前向算法forward algorithm2.3 ...
- DataTable定义
DataTable是一个临时保存数据的网格虚拟表(表示内存中数据的一个表.).DataTable是ADO dot net 库中的核心对象.它可以被应用在 VB 和 ASP 上.它无须代码就可以简单的绑 ...
- [BZOJ4212]神牛的养成计划
[BZOJ4212]神牛的养成计划 试题描述 Hzwer 成功培育出神牛细胞,可最终培育出的生物体却让他大失所望...... 后来,他从某同校女神 牛处知道,原来他培育的细胞发生了基因突变,原先决定神 ...
- [SDOI2016] 排列计数 (组合数学)
[SDOI2016]排列计数 题目描述 求有多少种长度为 n 的序列 A,满足以下条件: 1 ~ n 这 n 个数在序列中各出现了一次 若第 i 个数 A[i] 的值为 i,则称 i 是稳定的.序列恰 ...
- bzoj 4624 农场种植 fft
4624: 农场种植 Time Limit: 50 Sec Memory Limit: 512 MBSubmit: 48 Solved: 31[Submit][Status][Discuss] D ...
- css中文本超出部分省略号代替
p{ width: 100px; //设置p标签宽度 white-space: nowrap; //文本超出P标签宽度不换行,而是溢出 overflow: hidden; //文本超出P标签,超出部分 ...
- Notepad++64插件安装方法
首先通过https://github.com/bruderstein/nppPluginManager/releases下载"nppPluginManager",下载解压后放到对应 ...