浅谈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 ...
随机推荐
- POJ3984(迷宫问题)
定义一个二维数组: int maze[5][5] = { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, ...
- 基于bootstrap动态分页
bootstrap本身的分页有分页组件 但是却是静态的,无法满足要求,分页必须根据当前的总页数来展示 使用插件bootstrap-paginator github下载地址 https://github ...
- 推荐一个好的Redis GUI 客户端工具
推荐一个好的Redis GUI 客户端工具 Redis Desktop Manager
- 列数不固定时怎么使用el-tabel展示数据
<el-table :data="contents" stripe> <el-table-column v-for="(item, index) in ...
- AGC017C Snuke and Spells(巧妙的线段覆盖模型)
题目大意: 给出n个球,每个球上都有数字,然后每次都进行如下操作 如果当前的球总共有k个,那么就把球上数字为k的所有球都消除掉 注意到,并不是每种情况都可以全部消光,所以你可以选择若干球,把它们标号改 ...
- RDMA
什么是RDMA? 来源 https://blog.csdn.net/u011459120/article/details/78469098 1. 概述 RDMA是Remote Direct Memor ...
- 周记【距gdoi:126天】
这周比上周好了那么一点点……但还是有点呵呵 搞了仙人掌图(当然不是动态的……),以及一个远古算法2-sat(神奇的对称性运用,需要巨大脑洞的建边). 然后关于高考不加分竞赛被“打压”……大神们都发表了 ...
- SLF4J 与Log4J
为什么要使用SLF4J而不是Log4J 每一个Java程序员都知道日志对于任何一个Java应用程序,尤其是服务端程序是至关重要的,而很多程序员也已经熟悉各种不同的日志库如java.util.loggi ...
- html中音频和视频
HTML5音频中的新元素标签 src:音频文件路径. autobuffer:设置是否在页面加载时自动缓冲音频. autoplay:设置音频是否自动播放. loop:设置音频是否要循环播放. contr ...
- Python爬虫学习笔记之爬今日头条的街拍图片
代码: import requests import os from hashlib import md5 from urllib.parse import urlencode from multip ...