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)的更多相关文章

  1. C#中的线程(三) 使用多线程

    第三部分:使用多线程 1.  单元模式和Windows Forms 单元模式线程是一个自动线程安全机制, 非常贴近于COM——Microsoft的遗留下的组件对象模型.尽管.NET最大地放弃摆脱了遗留 ...

  2. android中的线程池学习笔记

    阅读书籍: Android开发艺术探索 Android开发进阶从小工到专家 对线程池原理的简单理解: 创建多个线程并且进行管理,提交的任务会被线程池指派给其中的线程进行执行,通过线程池的统一调度和管理 ...

  3. Windows API学习---用户方式中的线程同步

    前言 当所有的线程在互相之间不需要进行通信的情况下就能够顺利地运行时, Micrsoft Windows的运行性能最好.但是,线程很少能够在所有的时间都独立地进行操作.通常情况下,要生成一些线程来处理 ...

  4. JAVA中创建线程的三种方法及比较

    JAVA中创建线程的方式有三种,各有优缺点,具体如下: 一.继承Thread类来创建线程 1.创建一个任务类,继承Thread线程类,因为Thread类已经实现了Runnable接口,然后重写run( ...

  5. Java中创建线程的三种方式以及区别

    在java中如果要创建线程的话,一般有3种方法: 继承Thread类: 实现Runnable接口: 使用Callable和Future创建线程. 1. 继承Thread类 继承Thread类的话,必须 ...

  6. {Python之线程} 一 背景知识 二 线程与进程的关系 三 线程的特点 四 线程的实际应用场景 五 内存中的线程 六 用户级线程和内核级线程(了解) 七 python与线程 八 Threading模块 九 锁 十 信号量 十一 事件Event 十二 条件Condition(了解) 十三 定时器

    Python之线程 线程 本节目录 一 背景知识 二 线程与进程的关系 三 线程的特点 四 线程的实际应用场景 五 内存中的线程 六 用户级线程和内核级线程(了解) 七 python与线程 八 Thr ...

  7. C#中的线程(三)多线程

    C#中的线程(三)多线程   Keywords:C# 线程Source:http://www.albahari.com/threading/Author: Joe AlbahariTranslator ...

  8. java中终止线程的三种方式

    在java中有三种方式可以终止线程.分别为: 1.  使用退出标志,使线程正常退出,也就是当run方法完成后线程终止.  2.  使用stop方法强行终止线程(这个方法不推荐使用,因为stop和sus ...

  9. Java中的线程

    http://hi.baidu.com/ochzqvztdbabcir/item/ab9758f9cfab6a5ac9f337d4 相濡以沫 Java语法总结 - 线程 一 提到线程好像是件很麻烦很复 ...

随机推荐

  1. ORACLE存储过程创建失败,如何查看其原因

    工作中用SQL Server比较多,Oracle可以说是小白,最近想用存储过程来完成单据复制的功能,结果遇到各种问题,其实都是非常简单的问题,但是对我来说还是花了很多时间来解决,浪费这些时间非常不值得 ...

  2. oracle 体系结构

    oracle 体系结构 数据库的体系结构是指数据库的组成.工作过程与原理,以及数据在数据库中的组织与管理机制. 1. oracle工作原理: 1).在数据库服务器上启动Oracle实例:2).应用程序 ...

  3. 进阶版css的点滴

    -moz-:firefox: -webkit- Safari 和 Chrome

  4. .Net中的加密解密

    返回博客列表 转 .Net中的加密解密 李朝强 发布时间: 2015/11/23 12:55 阅读: 33 收藏: 3 点赞: 0 评论: 0 在一些比较重要的应用场景中,通过网络传递数据需要进行加密 ...

  5. Jquery插件开发学习

    一:导言 有些WEB开发者,会引用一个JQuery类库,然后在网页上写一写$("#"),$("."),写了几年就对别人说非常熟悉JQuery.我曾经也是这样的人 ...

  6. 关于HTTP session随笔

    1).访问*.html的静态资源因为不会被编译为Servlet,也就不涉及session的问题. 2).当JSP页面没有显式禁止session的时候,在打开浏览器第一次请求该jsp的时候,服务器会自动 ...

  7. js高阶函数

    我是一个对js还不是很精通的选手: 关于高阶函数详细的解释 一个高阶函数需要满足的条件(任选其一即可) 1:函数可以作为参数被传递 2:函数可以作为返回值输出 吧函数作为参数传递,这代表我们可以抽离一 ...

  8. AngularJS 页面基本操作

    一.避免预先加载angular模板语法 <style> [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, ...

  9. Echarts给柱状图每一条顶部都添加数值

    在 series中添加 itemStyle : { normal: {label : {show: true}}}, 不过目测要把markPoint等去掉,不然最大值和最小值会和label冲突,应该有 ...

  10. Linux下连接MSSQL之安装FreeTDS

    $ tsql -H MSSQL服务器服务IP -p 1433 -U MSSQL服务器登陆帐号 -P MSSQL服务器登陆密码http://www.cnblogs.com/ilovexiao/p/355 ...