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. POJ 3469 Dual Core CPU 最大流

    划分成两个集合使费用最小,可以转成最小割,既最大流. //#pragma comment(linker, "/STACK:1024000000,1024000000") #incl ...

  2. windows 8.1 下蓝屏报错:SYSTEM_SERVICE_EXCEPTION(NETIO.SYS)的解决办法

         大概2周前,电脑突然蓝屏了,我上网查了一下解决办法,因为大部分内容是英文的,所以我只大概看了下,看到这个问题好像是由于软件冲突造成的,于是就把小红伞去掉了,而那天电脑也真的没有再蓝屏(之前大 ...

  3. jQuery管理包装集笔记

    size():返回包装集中元素的个数. get([n]):返回一个DOM元素或DOM元素数组(接受负值). toArray():将包装里的所有元素作为DOM元素数组返回. eq(n):获取包装集中与i ...

  4. js严格模式总结(转~)原文地址http://www.cnblogs.com/chayan/p/5793964.html

    首页,我们要理解严格模式的概念,严格模式是一种特殊的执行模式,它修复了部分语言上的不足,提供更强的错误检查,病增强安全性.可以对部分函数进行执行严格模式,如: function func(){ 'us ...

  5. js高级应用

    特别板块:js跨域请求Tomcat6.tomcat7 跨域设置(包含html5 的CORS) 需要下载两个jar文件,cors-filter-1.7.jar,Java-property-utils-1 ...

  6. Layout in Rails

    参考链接:http://guides.rubyonrails.org/layouts_and_rendering.html#structuring-layouts layout layout最基本的使 ...

  7. JSTL和EL表达式多重if问题

    俾人以前在写一个查询功能时,由于结果状态分好几种,于是页面就用<c:if></c:if>写了一大堆来判断,后来上网查了下资料,发现有个语法类似于多重if,挺方便的,语法是 &l ...

  8. UI基础之UIButton相关

    UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; 1.按钮透明效果 btn.alpha = 0.8; 2.按钮圆角处理 btn ...

  9. STM32 使用DMA+DAC+TIMER 输出正弦波

    之前已经简单论述过,根据我个人菜鸟的了解与认识,对之前的知识进行整理回顾: DMA:我的理解就是一个通道,或者是一座桥梁.在静态内存到静态内存,或者外设到静态内存间的一个通讯的通道.建立这个通道的好处 ...

  10. Block循环引用问题研究

    自从苹果在objc中添加Block功能支持以后已经过了很久.目前网上对于Block的使用有很多介绍.不过对于Block的内存管理问题,则是众说纷纭.再加上objc开始使用ARC以后,对于Block的内 ...