C#中的线程三 (结合ProgressBar学习Control.BeginInvoke)
C#中的线程三(结合ProgressBar学习Control.BeginInvoke)
本篇继上篇转载的关于Control.BeginInvoke的论述之后,再结合一个实例来说明Cotrol.BeginInvoke的功能
通过前面2篇的学习应该得出以下结论
1、Delegate.BeginInvoke中执行的方法是异步的
public static void Start2()
{
Console.WriteLine("main thread:{0},{1},{2}", Thread.CurrentThread.CurrentCulture, Thread.CurrentThread.Name, Thread.CurrentThread.ManagedThreadId);
//DoSomethingDelegate del = new DoSomethingDelegate(Method1);
DoSomethingDelegate del = Method1;
del.BeginInvoke("this is delegate method", null,null) Console.WriteLine("main thread other things...");
}
相当于另开了一个线程来执行Method1方法
2. 如果在UI线程里做Control.BeginInvoke,执行到的方法并没有做到异步
private void butBeginInvoke_Click(object sender, EventArgs e) {
//A代码段.......
this.BeginInvoke(new BeginInvokeDelegate(BeginInvokeMethod));
//B代码段......
}
也就是说此段代码里,BeginInvokeMethod方法并没有异步执行到,也即没有新开线程做BeginInvokeMethod这个方法
3.如果要让Control.BeginInvoke做到异步,需要在UI线程里新开一个线程,在这个新开的线程里调用Control.BeginInvoke,才能有异步功能
private Thread beginInvokeThread;
private delegate void beginInvokeDelegate();
private void StartMethod(){
//C代码段......
Control.BeginInvoke(new beginInvokeDelegate(beginInvokeMethod));
//D代码段......
}
private void beginInvokeMethod(){
//E代码段
}
private void butBeginInvoke_Click(object sender, EventArgs e) {
//A代码段.......
beginInvokeThread = new Thread(new ThreadStart(StartMethod));
beginInvokeThread .Start();
//B代码段......
}
有了上面的理解,我们结合实例来继续学习Control.BeginInvoke
二、ProgressBar的使用
WinForm里有这样一种场景,就是一边处理数据,一边用ProgressBar显示进度,要做出这种功能来如果还是用单线程那种普通的思路,按顺序编码下来,进度条就会从0突然变成100,失去了进度功能。
1.假如我们现在要读取大量数据, 需要用ProgressBar来显示进度,我们先定义一个类,此类中有一个方法Work是用来读取数据的!
public class ProgressBarWork
{
public void Work()
{
int iTotal = ;//计算工作量
int iCount = ;
for (int i = ; i < iTotal; i++)
{
System.Threading.Thread.Sleep();//模拟工作 iCount = i;
}
} }
继续完善此方法,这个方法虽然完成了读取数据的任务,但是如何让外部的ProgressBar知道此方法执行的状态呢?
答案是:该方法从开始执行,中间每次读取,执行完毕要暴露出3个事件来,通知在这三种状态下,ProgressBar应该如何显示,为此我们要声明一个委托,三个事件!
既然要用到事件,还需要用到自定义的EventArgs来传递状态,为此我们定义一个EventArgs
public class WorkEventArgs : EventArgs
{
//主要是用来往外传递信息的
public WorkStage Stage;
public string Info = "";
public int Position = ;
public WorkEventArgs(WorkStage Stage, string Info, int Position)
{
this.Stage = Stage;
this.Info = Info;
this.Position = Position;
}
}
public enum WorkStage
{
BeginWork, //准备工作
DoWork, //正在工作
EndWork, //工作结束
}
接着在ProgressBarWork类中定义事件
public delegate void PBWorkEventHandler(object sender, WorkEventArgs e);
public class ProgressBarWork
{
//开始事件
public event PBWorkEventHandler OnStartWorkEvent;
//执行事件
public event PBWorkEventHandler OnDoWorkEvent;
//结束事件
public event PBWorkEventHandler OnEndWorkEvent; public void Work()
{
int iTotal = ;//计算工作量
int iCount = ;
SendEvents(new WorkEventArgs(WorkStage.BeginWork, "start work", iTotal));
for (int i = ; i < iTotal; i++)
{
System.Threading.Thread.Sleep();//模拟工作
iCount = i;
SendEvents(new WorkEventArgs(WorkStage.DoWork, "working" + iCount.ToString(), iCount));
}
SendEvents(new WorkEventArgs(WorkStage.EndWork, "end work", iTotal));
} private void SendEvents(WorkEventArgs e)
{
switch (e.Stage)
{
case WorkStage.BeginWork:
if (OnStartWorkEvent != null) OnStartWorkEvent(this, e);
break;
case WorkStage.DoWork:
if (OnDoWorkEvent != null) OnDoWorkEvent(this, e);
break;
case WorkStage.EndWork:
if (OnEndWorkEvent != null) OnEndWorkEvent(this, e);
break;
default:
if (OnDoWorkEvent != null) OnDoWorkEvent(this, e);
break; }
}
这样就在"方法执行前","执行中","执行结束"这三种状态下暴露了三个不同的事件!我们要在这三个不同的事件下,控制ProgressBar的状态
拿"方法执行前"来说,我们需要在UI线程中做如下编码
ProgressBarWork work = new ProgressBarWork();
//订阅事件
work.OnStartWorkEvent += new PBWorkEventHandler(WorkStart);
//其他事件订阅...
然后调用work.Work()方法来开始读取数据,我们通过前面的学习可以得知,如果要做到ProgressBar的显示和数据处理同步,必须单独开一个线程来做数据处理
System.Threading.ThreadStart startWork = work.Work;
System.Threading.Thread thread = new System.Threading.Thread(startWork);
thread.Start();
这样在WorkStart方法中就可以设置ProgressBar中的初始值了
void WorkStart(object sender, WorkEventArgs e)
{
this.statusProgressBar.Maximum = e.Position;
this.statusProgressBar.Value = ;
}
如果按照单线程的思想,这样编码是没有问题的,但是如果运行这段程序,会抛出如下异常!

什么原因?这里的this.statusProgressBar是不会运行成功的,因为这个方法不是有UI线程来调用的,必须通过control.Invoke来给ProgressBar赋值!
void WorkStart(object sender, WorkEventArgs e)
{
PBWorkEventHandler del = SetMaxValue;
this.BeginInvoke(del, new object[] { sender, e });
}
private void SetMaxValue(object sender, WorkEventArgs e)
{
this.statusProgressBar.Maximum = e.Position;
this.statusProgressBar.Value = ;
}
这样就确保了SetMaxValue方法是在UI线程中执行的
UI界面完整代码如下:
private void ImitateProgressBar()
{
ProgressBarWork work = new ProgressBarWork();
//订阅事件
work.OnStartWorkEvent += new PBWorkEventHandler(WorkStart);
work.OnDoWorkEvent += new PBWorkEventHandler(Working);
work.OnEndWorkEvent += new PBWorkEventHandler(WorkEnd);
System.Threading.ThreadStart startWork = work.Work;
System.Threading.Thread thread = new System.Threading.Thread(startWork);
thread.Start();
} void WorkStart(object sender, WorkEventArgs e)
{
PBWorkEventHandler del = SetMaxValue;
this.BeginInvoke(del, new object[] { sender, e }); }
private void SetMaxValue(object sender, WorkEventArgs e)
{
this.statusProgressBar.Maximum = e.Position;
this.statusProgressBar.Value = ;
}
void Working(object sender, WorkEventArgs e)
{
PBWorkEventHandler del = SetNowValue;
this.BeginInvoke(del, new object[] { sender, e });
}
private void SetNowValue(object sender, WorkEventArgs e)
{
this.statusProgressBar.Value = e.Position;
} void WorkEnd(object sender, WorkEventArgs e)
{
PBWorkEventHandler del = SetEndValue;
this.BeginInvoke(del, new object[] { sender, e });
}
private void SetEndValue(object sender, WorkEventArgs e)
{
this.statusProgressBar.Value = e.Position; }
其实在以上代码中,我们还可以通过this.Invoke(del, new object[] { sender, e });来调用,效果是跟this.BeginInvoke(del, new object[] { sender, e });调用时一样的,因为
void WorkStart(object sender, WorkEventArgs e)
{
PBWorkEventHandler del = SetMaxValue;
this.Invoke(del, new object[] { sender, e }); }
这个方法已经是在新开的线程上执行的,只要确保在新的线程上利用UI线程去给ProgressBar赋值即可
三、在这个例子中我们还可以看出Control.Invoke和Control.BeginInvoke这两个方法的重要功能:
是在多线程的环境下 确保用UI线程去执行一些调用控件的方法,因为其他线程无法访问UI控件!!!!,这是上篇文章所没有提到的!
C#中的线程三 (结合ProgressBar学习Control.BeginInvoke)的更多相关文章
- C#中的线程(三) 使用多线程
第三部分:使用多线程 1. 单元模式和Windows Forms 单元模式线程是一个自动线程安全机制, 非常贴近于COM——Microsoft的遗留下的组件对象模型.尽管.NET最大地放弃摆脱了遗留 ...
- android中的线程池学习笔记
阅读书籍: Android开发艺术探索 Android开发进阶从小工到专家 对线程池原理的简单理解: 创建多个线程并且进行管理,提交的任务会被线程池指派给其中的线程进行执行,通过线程池的统一调度和管理 ...
- Windows API学习---用户方式中的线程同步
前言 当所有的线程在互相之间不需要进行通信的情况下就能够顺利地运行时, Micrsoft Windows的运行性能最好.但是,线程很少能够在所有的时间都独立地进行操作.通常情况下,要生成一些线程来处理 ...
- JAVA中创建线程的三种方法及比较
JAVA中创建线程的方式有三种,各有优缺点,具体如下: 一.继承Thread类来创建线程 1.创建一个任务类,继承Thread线程类,因为Thread类已经实现了Runnable接口,然后重写run( ...
- Java中创建线程的三种方式以及区别
在java中如果要创建线程的话,一般有3种方法: 继承Thread类: 实现Runnable接口: 使用Callable和Future创建线程. 1. 继承Thread类 继承Thread类的话,必须 ...
- {Python之线程} 一 背景知识 二 线程与进程的关系 三 线程的特点 四 线程的实际应用场景 五 内存中的线程 六 用户级线程和内核级线程(了解) 七 python与线程 八 Threading模块 九 锁 十 信号量 十一 事件Event 十二 条件Condition(了解) 十三 定时器
Python之线程 线程 本节目录 一 背景知识 二 线程与进程的关系 三 线程的特点 四 线程的实际应用场景 五 内存中的线程 六 用户级线程和内核级线程(了解) 七 python与线程 八 Thr ...
- C#中的线程(三)多线程
C#中的线程(三)多线程 Keywords:C# 线程Source:http://www.albahari.com/threading/Author: Joe AlbahariTranslator ...
- java中终止线程的三种方式
在java中有三种方式可以终止线程.分别为: 1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止. 2. 使用stop方法强行终止线程(这个方法不推荐使用,因为stop和sus ...
- Java中的线程
http://hi.baidu.com/ochzqvztdbabcir/item/ab9758f9cfab6a5ac9f337d4 相濡以沫 Java语法总结 - 线程 一 提到线程好像是件很麻烦很复 ...
随机推荐
- shell 环境初始化顺序
登陆shell 的执行顺序 /etc/profile /etc/profile.d/file /etc/bashrc .bashrc .bash_profile 非登录shell 的执行顺序, 例如: ...
- keepalived高可用
keepalived介绍 Keepalived是一个基于vrrp协议的高可用方案,vrrp协议的软件实现,原生设计的目的为了高可用ipvs服务. 1. vrrp协议 VRRP是一种容错协议,它通过把几 ...
- XE3随想14:关于 SO 与 SA 函数
通过 SuperObject 的公用函数 SO 实现一个 ISuperObject 接口非常方便; 前面都是给它一个字符串参数, 它的参数可以是任一类型甚至是常数数组. SA 和 SO 都是返回一 I ...
- 如何用Tacker将NFV带入OpenStack?
最初社区里很多人争论过NFV是否属于OpenStack,而后来可以确定的是OpenStack的确占据了NFV会话中的很大一部分,并且形象地反映在了下面的ETSI MANO概念架构图中,OpenStac ...
- Powerdesigner 设置唯一约束
- TCP、UDP、HTTP、SOCKET之间的区别
IP:网络层协议: TCP和UDP:传输层协议: HTTP:应用层协议: SOCKET:TCP/IP网络的API. TCP/IP代表传输控制协议/网际协议,指的是一系列协议. TCP和UDP使用IP协 ...
- [09]APUE:进程关系
[a] getpgid / setpgid #include <unistd.h> pid_t getpgid(pid_t pid) //成功返回进程组 ID,出错返回 -1 int se ...
- DIV弹出和关闭新DIV
代码用HTML+JS实现: 代码(用HTML+JS实现): <!doctype html> <html lang="UTF-8"> <head> ...
- WebView 上传文件 WebChromeClient之openFileChooser函数
原链接:http://blog.saymagic.cn/2015/11/08/webview-upload.html?utm_source=tuicool&utm_medium=referra ...
- 原因是出现以下错误: 80040154 没有注册类 (异常来自 HRESULT:0x80040154 (REGDB_E_CLASSNOTREG))
场景: 客户端初始化正常,到IIS服务就报80040154. System.Web.Services.Protocols.SoapException: System.Web.Services.Prot ...