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