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. leetcode解题:Add binary问题

    顺便把之前做过的一个简单难度的题也贴上来吧 67. Add Binary Given two binary strings, return their sum (also a binary strin ...

  2. IosPush推送通知的实现

    1. Apple推送通知的机制 上图可以分为三个阶段: 第一阶段:应用程序把要发送的消息.目的iPhone的标识打包,发给APNS. 第二阶段:APNS在自身的已注册Push服务的iPhone列表中, ...

  3. savepic

    #-*-coding:utf-8-*-from lxml import etreeimport requestsfrom urllib import urlretrieveimport osdef g ...

  4. jsp中,个别乱码进行转码操作

    来自大神 if(xh!=null && xh!=""){ xhmc =new String(xh.getBytes("ISO-8859-1"), ...

  5. React阶段开发总结

    这次独立编写了React页面主要是数据切换.点击不同的按钮,Ajax请求不同的后台数据.数据驱动表格内容的显示.使用React组件开发. 开发中获得下面的心得: 1.后台给的地址早一点添加路由(写好数 ...

  6. SVN系统的几个术语

    SVN系统的几个术语 User:用户,可以远程连接到SVN服务器的权限实体. User Group:用户组,用于管理一组权限相同的用户. Repository:版本库,在服务器端保存着的项目中所有的文 ...

  7. floyd算法小结

    floyd算法是被大家熟知的最短路算法之一,利用动态规划的思想,f[i][j]记录i到j之间的最短距离,时间复杂度为O(n^3),虽然时间复杂度较高,但是由于可以处理其他相似的问题,有着广泛的应用,这 ...

  8. SIGGRAPH2016【转】

    本文摘自:http://blog.selfshadow.com/ Open Access SIGGRAPH 2016 Conference Content (for a limited time) R ...

  9. pt_table_checksum对检查表的chunk大小的限制

    02-16T10:22:38 Skipping table xoxdb.tb_valuelog because on the master it would be checksummed in one ...

  10. Matlab-Octave中绘制网格图和等高线:mesh 和 surf

    x=linspace(-50, 50, 50); % 在x轴上取50点y=linspace(-25, 25, 25); % 在y轴上取25点[xx,yy]=meshgrid(x, y); % xx和y ...