加入收藏 | 设为首页 | 会员中心 | 我要投稿 北几岛 (https://www.beijidao.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

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

发布时间:2021-07-06 06:18:42 所属栏目:大数据 来源: https://www.jb51.cc
导读:C#中的线程三(结合ProgressBar学习Control.BeginInvoke) ? ? 本篇继上篇转载的关于Control.BeginInvoke的论述之后,再结合一个实例来说明Cotrol.BeginInvoke的功能 ? ? 通过前面2篇的学习应该得出以下结论 1、Delegate.BeginInvoke中执行的方法是异步的 1 pu

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",nullnull)    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控件!!!!,这是上篇文章所没有提到的!

(编辑:北几岛)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读