通常我们在开发应用程序时如果遇到需要等待时间长或是进行大数量级的循环时,会用到进度条这个控件,这个控件可以显示出任务执行的进度,同时会给静态的窗体以动态的展示,让用户的等待有明确的目的,然而,对于进度条(ProgressBar)来说,能否正确的使用对应用程序的执行效率有着至关重要的意义。
稍有些开发经验的朋友会知道,进度条的更新是要配合系统控制权转移的操作的,在VB中就是DoEvents,在Delphi中则是Application.ProcessMessages,这两种语句是用来向系统交还控制权,以便系统处理其他事件,如果没有这样的操作,应用程序在处理耗时较多的任务时会进入“假死状态”。但是,又恰恰是这两个语句,会让你的应用程序效率变得非常低下,特别是在处理海量数据的时候,下面进行说明:
大家都知道,不论是VB还是Delphi,其ProgressBar都有Min和Max这两个属性,这两个属性标明了进度条控件的取值范围,其数据类型在VB中为Long,对应Delphi中的Integer;一般情况下,我们最容易想到的方法,包括在MSDN或Delphi的帮助信息中都提到的方法,就是把循环的变量起始值作为Min,把终止值作为Max,然后让进度条的value(Position)属性的值等于循环变量的值。如下例:
(VB)Dim i as Long
With ProgressBar1
.Min=1
.Max=12500000
End With
For i=1 to 12500000
ProgressBar1.Value=i
DoEvents
Next i
(Delphi)
Var
i:Integer;
Begin
With ProgressBar1 Do
Begin
Min:=1;
Max:=12500000;
End;
For i:=1 to 12500000do
Begin
ProgressBar1.Position:=i;
Application.ProcessMessages;
End;
End;
以上是同一个普通操作在VB和Delphi中的写法,执行效果完全一样,那么,我们先来看看有什么问题。
(1)假如,上述循环过程中的变量不是从1到1000,而是从1000000到了1001000呢,执行效率会是怎么样?(2) 假如,上述循环过程中的变量不是从1到1000,而是从1到了12500000呢?执行效率又会是如何?
有兴趣的朋友可以自己试试,并记录下进度条从头走到尾所消耗的时间。注意,这仅仅是让进度条走,任何其他操作都没有执行。那么可以试想一下,如果加上循环执行的其他操作呢?如读数据库或写文件等,就算每一次循环仅增加1毫秒的运行时间,那么1250万次循环会增加多少时间?答案是会增加12500秒,也就是3个小时!!!。
现在我们来对上面的程序运行进行分析和优化。
仔细阅读上述程序,会发现,每进行一次循环,应用程序都要更新一次进度条,真的有这个必要吗?进度条的目的仅仅是为了让用户的等待变得不再盲目,因此我们没有必要这么频繁的更新进度条,那么我们试着让进度条的更新不再那么频繁。重新编写上面的程序:
(VB)Dim i as Long
With ProgressBar1
.Min=1
.Max=12500000
End With
For i=1 to 12500000
If i Mod 100 = 0 then
ProgressBar1.Value=i
DoEvents
End if
Next i
(Delphi)
Var
i:Integer;
Begin
With ProgressBar1 Do
Begin
Min:=1;
Max:=12500000;
End;
For i:=1 to 12500000do
Begin
If i Mod 100 = 0 Then
Begin
ProgressBar1.Position:=i;
Application.ProcessMessages;
End;
End;
End;
这样,运行时减少到原来的1/100。再测一下运行时间。和前面实验记录的时间进行比较,会发现这个时间是少于前面两段未改良程序的时间的,但是!作为一个技术人员或是一个立志成为一个技术人员的四有青年的人来说,探索是无止境的!我们再想想,这个程序还能不能再快一些了??
那么这样分析,对于你自己的大脑来说,让你从1数到100容易些呢?还是让你从11432212数到11432312容易呢?虽然同样是数100个数字?相信任何正常的地球居民无任何脑部疾病的人都会觉得从1数到100容易些,那么对于电脑呢?同样是如此。那么我们对上面的程序再次进行优化,具体方案如下:
让进度条只在1到100的范围内活动,同时每隔100个循环才对进度条进行一次更新!
于是,程序就又被改写成了这样:
(VB)Dim i as Long
With ProgressBar1
.Min=1
.Max=100
End With
For i=1 to 12500000
If i Mod 100 = 0 then
ProgressBar1.Value=i * 100 / 12500000
DoEvents
End if
Next i
(Delphi)
Var
i:Integer;
Begin
With ProgressBar1 Do
Begin
Min:=1;
Max:=100;
End;
For i:=1 to 12500000do
Begin
If i Mod 100 = 0 Then
Begin
ProgressBar1.Position:=i * 100 Div 12500000;
Application.ProcessMessages;
End;
End;
End;
再次运行程序,相信各位都会有新的感觉,这次根本不需要去看表计量时间了,因为傻瓜都会看出来这次的速度提升是非常快的。
本人做应用开发有些年头,从VB开始,做过Delphi、Asp、.Net1.0、.Net2.0、C++,且组织设计过多个行业应用系统,自认为对优化代码有一定的功底,然而在一次做到海量数据处理的时候发现了一直以来存在的上述问题,由此引发了对自己几年来的开发经验的重新思考,考虑再三,估计有不少在应用开发领域奋斗的朋友们可能也会存在同样的问题,所以写下此文,谨以此文献给所有热爱研究技术且宅心仁厚的兄弟姐妹们,或许哪位前辈高人有更好的方法来优化,或许哪位高人对在下的言论颇有意见,欢迎一起探讨。
jialiu830205@163.com