博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C#中的线程三 (结合ProgressBar学习Control.BeginInvoke)
阅读量:4685 次
发布时间:2019-06-09

本文共 7711 字,大约阅读时间需要 25 分钟。

C#中的线程三(结合ProgressBar学习Control.BeginInvoke)

 

  本篇继上篇转载的关于Control.BeginInvoke的论述之后,再结合一个实例来说明Cotrol.BeginInvoke的功能

    通过前面2篇的学习应该得出以下结论

1、Delegate.BeginInvoke中执行的方法是异步的

1 public static void Start2()2  {3    Console.WriteLine("main thread:{0},{1},{2}", Thread.CurrentThread.CurrentCulture, Thread.CurrentThread.Name, Thread.CurrentThread.ManagedThreadId);4    //DoSomethingDelegate del = new DoSomethingDelegate(Method1);5     DoSomethingDelegate del = Method1;6     del.BeginInvoke("this is delegate method", null,null)    Console.WriteLine("main thread other things...");7  }

相当于另开了一个线程来执行Method1方法

2. 如果在UI线程里做Control.BeginInvoke,执行到的方法并没有做到异步

1 private void butBeginInvoke_Click(object sender, EventArgs e) {2    //A代码段.......3    this.BeginInvoke(new BeginInvokeDelegate(BeginInvokeMethod));4    //B代码段......5 }

也就是说此段代码里,BeginInvokeMethod方法并没有异步执行到,也即没有新开线程做BeginInvokeMethod这个方法

3.如果要让Control.BeginInvoke做到异步,需要在UI线程里新开一个线程,在这个新开的线程里调用Control.BeginInvoke,才能有异步功能

1 private Thread beginInvokeThread; 2 private delegate void beginInvokeDelegate(); 3 private void StartMethod(){ 4    //C代码段...... 5    Control.BeginInvoke(new beginInvokeDelegate(beginInvokeMethod)); 6   //D代码段...... 7 } 8 private void beginInvokeMethod(){ 9   //E代码段10 }11 private void butBeginInvoke_Click(object sender, EventArgs e) {12    //A代码段.......13    beginInvokeThread = new Thread(new ThreadStart(StartMethod));14    beginInvokeThread .Start();15    //B代码段......16 }

有了上面的理解,我们结合实例来继续学习Control.BeginInvoke

 二、ProgressBar的使用

     WinForm里有这样一种场景,就是一边处理数据,一边用ProgressBar显示进度,要做出这种功能来如果还是用单线程那种普通的思路,按顺序编码下来,进度条就会从0突然变成100,失去了进度功能。

1.假如我们现在要读取大量数据, 需要用ProgressBar来显示进度,我们先定义一个类,此类中有一个方法Work是用来读取数据的!

1 public class ProgressBarWork 2  { 3    public void Work() 4      { 5        int iTotal = 50;//计算工作量 6        int iCount = 0; 7        for (int i = 0; i < iTotal; i++) 8          {                  9              System.Threading.Thread.Sleep(6);//模拟工作                             iCount = i;10            }11       } }

继续完善此方法,这个方法虽然完成了读取数据的任务,但是如何让外部的ProgressBar知道此方法执行的状态呢?

答案是:该方法从开始执行,中间每次读取,执行完毕要暴露出3个事件来,通知在这三种状态下,ProgressBar应该如何显示,为此我们要声明一个委托,三个事件!

    既然要用到事件,还需要用到自定义的EventArgs来传递状态,为此我们定义一个EventArgs

1 public class WorkEventArgs : EventArgs 2     { 3         //主要是用来往外传递信息的 4         public WorkStage Stage; 5         public string Info = ""; 6         public int Position = 0; 7         public WorkEventArgs(WorkStage Stage, string Info, int Position) 8         { 9             this.Stage = Stage;10             this.Info = Info;11             this.Position = Position;12         }13     }14     public enum WorkStage15     {16         BeginWork,  //准备工作17         DoWork,     //正在工作  18         EndWork,    //工作结束19     }

接着在ProgressBarWork类中定义事件

1 public delegate void PBWorkEventHandler(object sender, WorkEventArgs e); 2     public class ProgressBarWork 3     { 4         //开始事件 5         public event PBWorkEventHandler OnStartWorkEvent; 6         //执行事件 7         public event PBWorkEventHandler OnDoWorkEvent; 8         //结束事件 9         public event PBWorkEventHandler OnEndWorkEvent;10 11         public void Work()12         {13             int iTotal = 50;//计算工作量14             int iCount = 0;15             SendEvents(new WorkEventArgs(WorkStage.BeginWork, "start work", iTotal));16             for (int i = 0; i < iTotal; i++)17             {18                 System.Threading.Thread.Sleep(6);//模拟工作19                 iCount = i;20                 SendEvents(new WorkEventArgs(WorkStage.DoWork, "working" + iCount.ToString(), iCount));21             }22             SendEvents(new WorkEventArgs(WorkStage.EndWork, "end work", iTotal));23         }24 25         private void SendEvents(WorkEventArgs e)26         {27             switch (e.Stage)28             {29                 case WorkStage.BeginWork:30                     if (OnStartWorkEvent != null) OnStartWorkEvent(this, e);31                     break;32                 case WorkStage.DoWork:33                     if (OnDoWorkEvent != null) OnDoWorkEvent(this, e);34                     break;35                 case WorkStage.EndWork:36                     if (OnEndWorkEvent != null) OnEndWorkEvent(this, e);37                     break;38                 default:39                     if (OnDoWorkEvent != null) OnDoWorkEvent(this, e);40                     break;41 42             }43         }

这样就在"方法执行前","执行中","执行结束"这三种状态下暴露了三个不同的事件!我们要在这三个不同的事件下,控制ProgressBar的状态

拿"方法执行前"来说,我们需要在UI线程中做如下编码

1 ProgressBarWork work = new ProgressBarWork();2  //订阅事件3  work.OnStartWorkEvent += new PBWorkEventHandler(WorkStart);4 //其他事件订阅...

然后调用work.Work()方法来开始读取数据,我们通过前面的学习可以得知,如果要做到ProgressBar的显示和数据处理同步,必须单独开一个线程来做数据处理

1 System.Threading.ThreadStart startWork = work.Work;2 System.Threading.Thread thread = new System.Threading.Thread(startWork);3 thread.Start();

这样在WorkStart方法中就可以设置ProgressBar中的初始值了

1  void WorkStart(object sender, WorkEventArgs e)2    {          3       this.statusProgressBar.Maximum = e.Position;4       this.statusProgressBar.Value = 0;5     }

如果按照单线程的思想,这样编码是没有问题的,但是如果运行这段程序,会抛出如下异常!

 

什么原因?这里的this.statusProgressBar是不会运行成功的,因为这个方法不是有UI线程来调用的,必须通过control.Invoke来给ProgressBar赋值!

1 void WorkStart(object sender, WorkEventArgs e) 2    { 3       PBWorkEventHandler del = SetMaxValue; 4       this.BeginInvoke(del, new object[] { sender, e });      5    } 6  private void SetMaxValue(object sender, WorkEventArgs e) 7    { 8       this.statusProgressBar.Maximum = e.Position; 9       this.statusProgressBar.Value = 0;10    }

 这样就确保了SetMaxValue方法是在UI线程中执行的

UI界面完整代码如下:

1 private void ImitateProgressBar() 2     { 3             ProgressBarWork work = new ProgressBarWork(); 4             //订阅事件 5             work.OnStartWorkEvent += new PBWorkEventHandler(WorkStart); 6             work.OnDoWorkEvent += new PBWorkEventHandler(Working); 7             work.OnEndWorkEvent += new PBWorkEventHandler(WorkEnd); 8             System.Threading.ThreadStart startWork = work.Work; 9             System.Threading.Thread thread = new System.Threading.Thread(startWork);10             thread.Start();11      }12 13         void WorkStart(object sender, WorkEventArgs e)14         {15             PBWorkEventHandler del = SetMaxValue;16             this.BeginInvoke(del, new object[] { sender, e });17           18         }19         private void SetMaxValue(object sender, WorkEventArgs e)20         {21             this.statusProgressBar.Maximum = e.Position;22             this.statusProgressBar.Value = 0;23         }24         void Working(object sender, WorkEventArgs e)25         {26             PBWorkEventHandler del = SetNowValue;27             this.BeginInvoke(del, new object[] { sender, e });28         }29         private void SetNowValue(object sender, WorkEventArgs e)30         {31             this.statusProgressBar.Value = e.Position;32         }33 34         void WorkEnd(object sender, WorkEventArgs e)35         {36             PBWorkEventHandler del = SetEndValue;37             this.BeginInvoke(del, new object[] { sender, e });38         }39         private void SetEndValue(object sender, WorkEventArgs e)40         {41             this.statusProgressBar.Value = e.Position;42 43         }

其实在以上代码中,我们还可以通过this.Invoke(del, new object[] { sender, e });来调用,效果是跟this.BeginInvoke(del, new object[] { sender, e });调用时一样的,因为

1  void WorkStart(object sender, WorkEventArgs e)2     {3             PBWorkEventHandler del = SetMaxValue;4             this.Invoke(del, new object[] { sender, e });5           6      }

这个方法已经是在新开的线程上执行的,只要确保在新的线程上利用UI线程去给ProgressBar赋值即可

三、在这个例子中我们还可以看出Control.Invoke和Control.BeginInvoke这两个方法的重要功能:

是在多线程的环境下 确保用UI线程去执行一些调用控件的方法,因为其他线程无法访问UI控件!!!!,这是上篇文章所没有提到的!

转载于:https://www.cnblogs.com/richieyang/p/3658096.html

你可能感兴趣的文章
[RxJS] Avoid mulit post requests by using shareReplay()
查看>>
C++和C#之间的数据类型对应关系
查看>>
模型分离(选做)
查看>>
LeetCode 242. Valid Anagram
查看>>
观察者模式------《Head First 设计模式》
查看>>
JSP表单提交乱码
查看>>
如何适应现代雇佣关系
查看>>
【BZOJ4592】[Shoi2015]脑洞治疗仪 线段树
查看>>
redis sentinel 读写分离
查看>>
团队项目(第五周)
查看>>
ElasticSearch6(三)-- Java API实现简单的增删改查
查看>>
选拔赛 I 点进来吧,这里有你想要的
查看>>
SQL 优化经验总结34条
查看>>
开源 视频会议 收藏
查看>>
核心J2EE模式 - 截取过滤器
查看>>
test1
查看>>
jquery ajax 局部table 刷新技术
查看>>
类的关联、组合、聚合关系
查看>>
binary hacks读数笔记(ld 链接讲解 二)
查看>>
SKPhysicsJoint类
查看>>