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. 在Sublime Text3上面更加方便愉快的写php

    写php代码,elcipse体积太大,开起来太麻烦,记事本又没有什么标识,稍不留神就会出现;没加.大括号没对全的尴尬情况.所以我选择使用Sublime. 推荐几个 sublime插件:sublimeL ...

  2. jsp文件引入js文件的方式(项目部署于web容器中)

    在页面中引入javascript文件的方式是多种多样的,本文介绍两种. 通过<script>标签插入js文件 通过这种方式引入的js,写对js文件和jsp文件的路径很重要.下面给出一个项目 ...

  3. Android 测试Handler的基本使用

    package com.sherlock.app_handler; import java.io.ByteArrayOutputStream; import java.io.InputStream; ...

  4. SQL联合更新(只要有关联字段就能执行更新!)

    update t1 set KCLX=t2.KCLX,KSFS=t2.KSFS from JX_PlannedCourse t1 inner join JX_Course t2 on t1.KCDM= ...

  5. linux下的触控板手势xSwipe and tag

    这个最初是采用的touchegg.开始没有效果,后来网上发现是因为需要禁用系统的2指3指操作参考 http://askubuntu.com/questions/266057/cant-get-touc ...

  6. 数据库(SQL SERVER)常用知识点

    1,连接数据库字符串 Data Source=192.168.1.249;Initial Catalog=bbx_uf_jiekou;User ID=sa;Password=123 Data Sour ...

  7. 【Java】JDBC连接数据库

    JDBC介绍 JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言 ...

  8. Guidelines for accessing OneDrive from an app

    Guidelines for accessing OneDrive from an app https://msdn.microsoft.com/en-us/library/windows/apps/ ...

  9. yaf框架学习笔记

    1.yaf框架支持简单的试图引擎,并且支持用户自定义视图引擎,比如smarty. 2.Yaf_Request_Http::getQuery  ,Yaf_Request_Http::getQuery ( ...

  10. LoopBackJS 之 文件上传下载——使用loopback-component-storage

    参考链接: http://loopback.io/doc/en/lb2/Storage-component.html#creating-a-storage-component-data-source ...