下文是本人在公司技术月刊发表的一片文章。
前几周,OceanRay在论坛中问了关于vb中实现线程的问题。当时我比较忙,只是匆匆给他找了一点参考资料。当巴乔找到我,让我为第一期电子月刊写一篇windows专题的时候,我首先想到了OceanRay的问题。当时,我踌躇满志地想花点时间,写出一个真正有效的VB的多线程例子。最后还是放弃了。果真如Daniel Appleman所说,尽管VB允许调用Windows API来做一些高级工作,但是,在VB中使用线程相关API是十分危险的。几个小时的程序调试,换来的是不停的莫名其妙的内存访问错误。因为我比较缺乏线程编程经验,不得已,换了一个简单一点的题目。 x$a絗*
在网上粗略地搜了一下,发现虽然有很多代码介绍了用Timer控件来实现动画效果,但是好像没有人试图用Timer控件来模拟线程。即便有相关的讨论(参考文献[3]),也存在很多质疑。根据以前项目的经验,我写了一个小例子,用VB的Timer控件来模拟多线程。 T螥8灋﹊慦
1.什么是多线程?多线程比单线程“快吗”? .麰+驛w(T
线程是操作系统运行程序的基本单元。单线程是指一个程序在一个线程中运行。多线程是指一个程序启动多个线程来“同时”执行代码片断。请注意这里的同时是加了引号的。因为那是人类的错觉而已。对于一个单CPU的计算机,根本不存在“同时”这个概念。因为同一时间点,CPU只能执行某个线程的一个指令。举个例子,你打算在程序中做10件相同的任务。如果每个任务要花费1秒。那么,在单线程程序中,总共花费的时间就是10秒。如果用户通过点击按钮来启动任务,那么,他就必须在10秒钟之后才能使用这个程序的其他功能。假如你把10件任务放在10个线程中来执行,效果完全不同了。操作系统会花费了1秒钟在这些线程之间进行调度。那么,所有任务执行所用的时间就是11秒。由于使用了多线程,用户点击按钮之后可以马上使用其它功能。所以,在运行时间上,多线程要比单线程慢。但是,在用户响应时间上,多线程遥遥领先。
2.Timer控件是在创建另外一个线程来执行代码的吗? 5泜(船
回答是否定的。让我们来看个例子就知道了。
Private Sub Timer1_Timer(Index As Integer) OO腉%
Dim i As Long p/{嗒$"祢
For i = 0 To 100000 谷8驛1竵癋
‘DoEvents /嗰魳荆@嶋
Text1.Text = CStr(i) S琇L3令<
Next i 6!>y踝<
End Sub
Timer1的Timer事件函数中,有一个大循环。
Private Sub Command1_Click() lM8&i6
MsgBox "Interrupted!" 鷂炑T漱
End Sub
妴y 柋
代码1
&篟z0趵
当Timer开始运行后,我们点击Command1,画面没有反应。当Timer1一次执行完毕之后,消息框突然弹了出来。 ak疙U齮*
结论:VB的代码都是运行在一个线程之中的。Timer控件也不例外。我们还可以做一个实验证明Timer并不是使用多线程。将Timer1的Interval属性设置为1(相当于1毫秒),结果,Timer1并没有一秒钟执行了1000次。实际的Interval是大循环执行所花费的时间。
3.DoEvents——VB的“软中断” y7L厸
当我们把代码1中DoEvents的注释去掉后,发现程序的表现不一样了。循环开始后,画面依然响应用户的动作。当点击Command1时,消息框立即弹出,Timer1的事件也立刻停止执行。当消息框关闭后,Timer1的时间又恢复执行。 嵠屟氲
结论:VB中的DoEvents关键字与线程中的Suspend或者Sleep的作用很相似。都是让当前代码段让出CPU时间给其他代码。不同之处在于线程是操作系统低层的支持,效率高;而DoEvents是利用windows的消息系统实现的“软中断”。
4.多Timer对多线程 r鎛$濡
通常的多线程程序,都创建不止一个线程。为了模拟真正的多线程,我们需要在代码中动态增加Timer的数量。
Private WithEvents newTimer As VB.Timer /簧Ni鲿T5
…… 儇t5l7
Set newTimer = Controls.Add("vb.Timer", "Timer2", Form1) 嶘!獍[2
…… 比錴愷咧
Private Sub newTimer_Timer() v鈗
End Sub 扐纵s"2&
……
y擁U侃Sx
代码2
通过如上方式,我们可以在Form1中动态添加Timer2。虽然,每个Timer执行的代码逻辑都可以不同,但是,由于所有的逻辑事先写好,所以不能实现真正的动态添加。后来,我找到了参考文献[4]中的代码,感觉可以实现真正的动态添加。遗憾的是时间仓促,没有来得及实践。
Private Sub Timer1_Timer() 葭]当G
work Timer1.Name @Tv=夿%H
End Sub
Private Sub newTimer_Timer() 舻徨E帚
work newTimer.Name 8瞻厑湆
End Sub
Private Sub work(name As String) 凓=2~飜邷
Debug.Print "in--------------------------" + name [5偳玙\N鴫
圔M垹x轟O
Dim i As Long 絏g;襝
For i = 0 To 1000 $辍q鋞Zz
DoEvents ^_漅cKZ
Text1.Text = CStr(i) S顫KI頢&&
Next i 捭4(滫%$
Debug.Print "out-------------------------" + name 獥q!
End Sub
蜮 .&^鎵Q
代码3
我在两个Timer事件中调用同一个函数,参数分别是两个Timer的名字。以下是控制台的输出: 寒夸|ぐ癕
…… 緵Z锖畗
in--------------------------Timer1 h湌=阃}
in--------------------------newTimer ﹩-銪
out-------------------------newTimer +堖54S2
out-------------------------Timer1 py4ò"
in--------------------------newTimer 邝獕nV昘掽
in--------------------------Timer1 ;V鈨歖喅
out-------------------------Timer1 歚杕I0豑'A
out-------------------------newTimer 寐鰰秎隄洧
in--------------------------Timer1 [ 韔8
in--------------------------newTimer 阢)uZ
out-------------------------newTimer 5荎薈@筤猑
out-------------------------Timer1 Z檭s柨
in--------------------------newTimer 8s*餉痥f検
in--------------------------Timer1
T|w 4
out-------------------------Timer1 ZUX艢=a
out-------------------------newTimer ,L-LBE醀q
in--------------------------Timer1 <^
PW<
in--------------------------newTimer 僁'窉揖
out-------------------------newTimer I,茈0"C[
out-------------------------Timer1 绿筭躊藵
……
z痕銴彉>
代码4
结论:通过在VB的Timer控件中灵活使用DoEvents,我们模拟多线程的效果。
5.Timer中的同步 [)溔蓯芙
在使用windows线程API的时候,我们可以使用信号灯或者临界区API来进行共享数据的同步。VB中没有这样的机制,需要我们在编程的时候根据信号灯的原理,来设置一些公共变量来充当同步标志。
后记: 弼X7從h
和OceanRay沟通后,了解到他们打算在VB中使用多线程是因为想把一个数据库操作逻辑封装函数放在线程中执行。因为这个函数在执行的时候是阻塞的,导致运行过程中程序不响应用户操作。而且用户还希望随时可以终止这个操作。他们设想如果把函数放在另外一个线程中执行,既可以让程序立即响应用户操作,又可以随时随地通过杀死线程的API来结束操作。这样看来,本文的方法对他们不会有太大帮助。因为如果想终止Timer的执行,必须等待Timer事件的逻辑全部执行完毕之后才行。看来,我们还得想点别的办法。也许,我们可以在下期月刊可以找到答案! 犝奔|4w鯋
期待大家的指正!
o~,QL*禹
参考文献:
[1]A Thread to Visual Basic: Multi-Threading In VB5 and VB6 Daniel Appleman http://www.freevbcode.com/ShowCode.Asp?ID=1287 橪腜纖
[2]《Advanced Visual Basic》 Matthew Curland 疚T/@竆/
[3]http://bbs.gameres.com/showthread.asp?threadid=47556 [$/頒
[4]http://www.vbsight.com/Zips/ControlArrayAtRuntime.zip 4鎈[m愤 1