C#中的线程三 (结合ProgressBar学习Control.BeginInvoke)
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,执行到的方法并没有做到异步 private void butBeginInvoke_Click(object sender,EventArgs e) { 2 A代码段....... 3 this.BeginInvoke(new BeginInvokeDelegate(BeginInvokeMethod)); B代码段...... 5 } 也就是说此段代码里,BeginInvokeMethod方法并没有异步执行到,也即没有新开线程做BeginInvokeMethod这个方法 3.如果要让Control.BeginInvoke做到异步,需要在UI线程里新开一个线程,在这个新开的线程里调用Control.BeginInvoke,才能有异步功能 1 private Thread beginInvokeThread; 2 delegate beginInvokeDelegate(); 3 StartMethod(){ 4 C代码段...... 5 Control.BeginInvoke( beginInvokeDelegate(beginInvokeMethod)); 6 D代码段...... 7 } 8 beginInvokeMethod(){ 9 E代码段 10 11 12 13 beginInvokeThread = new Thread( ThreadStart(StartMethod)); 14 beginInvokeThread .Start(); 15 16 } 有了上面的理解,我们结合实例来继续学习Control.BeginInvoke ?二、ProgressBar的使用 ???? WinForm里有这样一种场景,就是一边处理数据,一边用ProgressBar显示进度,要做出这种功能来如果还是用单线程那种普通的思路,按顺序编码下来,进度条就会从0突然变成100,失去了进度功能。 1.假如我们现在要读取大量数据, 需要用ProgressBar来显示进度,我们先定义一个类,此类中有一个方法Work是用来读取数据的! class ProgressBarWork 3 Work() 4 { 5 int iTotal = 50;计算工作量 6 int iCount = 0; 7 for (int i = 0; i < iTotal; i++) { 9 System.Threading.Thread.Sleep(6);模拟工作 iCount = i; } 11 } } 继续完善此方法,这个方法虽然完成了读取数据的任务,但是如何让外部的ProgressBar知道此方法执行的状态呢? 答案是:该方法从开始执行,中间每次读取,执行完毕要暴露出3个事件来,通知在这三种状态下,ProgressBar应该如何显示,为此我们要声明一个委托,三个事件! ??? 既然要用到事件,还需要用到自定义的EventArgs来传递状态,为此我们定义一个EventArgs WorkEventArgs : EventArgs { 3 主要是用来往外传递信息的 4 public WorkStage Stage; 5 string Info = "" 6 int Position = 7 public WorkEventArgs(WorkStage Stage,1)">string Info,1)">int Position) { 9 this.Stage = Stage; 10 this.Info = Info; 11 this.Position = Position; 12 } 13 } 14 enum WorkStage 15 16 BeginWork,1)">准备工作 17 DoWork,1)">正在工作 18 EndWork,1)">工作结束 19 } 接着在ProgressBarWork类中定义事件 void PBWorkEventHandler( 2 开始事件 event PBWorkEventHandler OnStartWorkEvent; 执行事件 PBWorkEventHandler OnDoWorkEvent; 8 结束事件 9 PBWorkEventHandler OnEndWorkEvent; 10 11 13 14 15 SendEvents(new WorkEventArgs(WorkStage.BeginWork,start work16 17 { 18 System.Threading.Thread.Sleep(模拟工作 19 iCount = i; 20 SendEvents(new WorkEventArgs(WorkStage.DoWork,1)">working" + iCount.ToString(),iCount)); 21 } 22 SendEvents(new WorkEventArgs(WorkStage.EndWork,1)">end work23 24 25 SendEvents(WorkEventArgs e) 26 27 switch (e.Stage) 28 29 case WorkStage.BeginWork: 30 if (OnStartWorkEvent != null) OnStartWorkEvent(this31 break32 WorkStage.DoWork: 33 if (OnDoWorkEvent != null) OnDoWorkEvent(34 35 WorkStage.EndWork: 36 if (OnEndWorkEvent != null) OnEndWorkEvent(37 38 default: 39 40 41 42 43 } 这样就在"方法执行前","执行中","执行结束"这三种状态下暴露了三个不同的事件!我们要在这三个不同的事件下,控制ProgressBar的状态 拿"方法执行前"来说,我们需要在UI线程中做如下编码 1 ProgressBarWork work = ProgressBarWork(); 2 订阅事件 3 work.OnStartWorkEvent += PBWorkEventHandler(WorkStart); 4 其他事件订阅... 然后调用work.Work()方法来开始读取数据,我们通过前面的学习可以得知,如果要做到ProgressBar的显示和数据处理同步,必须单独开一个线程来做数据处理 1 System.Threading.ThreadStart startWork = work.Work; 2 System.Threading.Thread thread = System.Threading.Thread(startWork); 3 thread.Start(); 这样在WorkStart方法中就可以设置ProgressBar中的初始值了 1 void WorkStart( { 3 this.statusProgressBar.Maximum = e.Position; 4 this.statusProgressBar.Value = 5 } 如果按照单线程的思想,这样编码是没有问题的,但是如果运行这段程序,会抛出如下异常! ? 什么原因?这里的this.statusProgressBar是不会运行成功的,因为这个方法不是有UI线程来调用的,必须通过control.Invoke来给ProgressBar赋值! { 3 PBWorkEventHandler del = SetMaxValue; 4 this.BeginInvoke(del,1)">new [] { sender,e }); 5 } 6 void SetMaxValue( 8 9 10 } ?这样就确保了SetMaxValue方法是在UI线程中执行的 UI界面完整代码如下: ImitateProgressBar() 3 ProgressBarWork work = 4 5 work.OnStartWorkEvent += 6 work.OnDoWorkEvent += PBWorkEventHandler(Working); 7 work.OnEndWorkEvent += PBWorkEventHandler(WorkEnd); 8 System.Threading.ThreadStart startWork = 9 System.Threading.Thread thread = thread.Start(); } 12 13 15 PBWorkEventHandler del =17 18 19 20 21 22 24 void Working(25 26 PBWorkEventHandler del = SetNowValue; 29 void SetNowValue(30 31 this.statusProgressBar.Value =32 33 34 void WorkEnd(35 36 PBWorkEventHandler del = SetEndValue; 37 38 39 void SetEndValue(40 41 42 43 } 其实在以上代码中,我们还可以通过this.Invoke(del,?new?object[]?{?sender,?e?});来调用,效果是跟this.BeginInvoke(del,?e?});调用时一样的,因为 3 PBWorkEventHandler del =4 this.Invoke(del,1)">5 6 } 这个方法已经是在新开的线程上执行的,只要确保在新的线程上利用UI线程去给ProgressBar赋值即可 三、在这个例子中我们还可以看出Control.Invoke和Control.BeginInvoke这两个方法的重要功能: 是在多线程的环境下 确保用UI线程去执行一些调用控件的方法,因为其他线程无法访问UI控件!!!!,这是上篇文章所没有提到的! (编辑:北几岛) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |