浅谈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 ...
随机推荐
- 列数不固定时怎么使用el-tabel展示数据
<el-table :data="contents" stripe> <el-table-column v-for="(item, index) in ...
- C++关于堆的函数
建立堆 make_heap(_First, _Last, _Comp) 默认是建立最大堆的.对int类型,可以在第三个参数传入greater<int>()得到最小堆. 在堆中添加数据 ...
- Elasticsearch中的分词器比较及使用方法
Elasticsearch 默认分词器和中分分词器之间的比较及使用方法 https://segmentfault.com/a/1190000012553894 介绍:ElasticSearch 是一个 ...
- [NOI.AC省选模拟赛3.30] Mas的童年 [二进制乱搞]
题面 传送门 思路 这题其实蛮好想的......就是我考试的时候zz了,一直没有想到标记过的可以不再标记,总复杂度是$O(n)$ 首先我们求个前缀和,那么$ans_i=max(pre[j]+pre[i ...
- [Leetcode] Roman to integer 罗马数字转成整数
Given a roman numeral, convert it to an integer. Input is guaranteed to be within the range from 1 t ...
- BZOJ1452 [JSOI2009]Count 【树套树 (树状数组)】
1452: [JSOI2009]Count Time Limit: 10 Sec Memory Limit: 64 MB Submit: 2693 Solved: 1574 [Submit][St ...
- 树剖模板by fcdalao
#include<bits/stdc++.h> using namespace std; ; *MX]; *MX]; int n,Index,fir[MX],fa[MX],dfn[MX], ...
- taotao购物车
功能分析: 1.在用户不登陆的情况下也可以使用购物车,那么就需要把购物车信息放入cookie中. 2.可以把商品信息,存放到pojo中,然后序列化成json存入cookie中. 3.取商品信息可以从c ...
- 怎么替换jar包里面的文件?
很多时候,我们需要替换包含在jar包里面的文件,例如修改里面的配置文件. 由于jar包已经生成,在不想重新用eclipse导出的情况下,我们怎么修改jar包里面的文件呢? 其实说出来很简单,可以使用以 ...
- Wpremig和Jhadgre的藏宝图(最大流+二分)
Wpremig和Jhadgre的藏宝图 题目链接:https://ac.nowcoder.com/acm/contest/333/M Description: Jhadgre在生日那天收到了一张神秘的 ...