VB.NET多线程应用
创始人
2024-02-09 06:23:24
0

开发者一直要求微软为VB加入更多的多线程功能,对于VB.NET也是这样。VB6已经支持建立多线程的EXE、DLL和OCX。不过使用多线程这个词语,可能也不太确切。因此VB6仅支持运行多个单线程的单元。一个单元实际上是代码执行的空间,而单元的边界限制了代码访问任何单元以外的事物。

VB.NET就不同了,它支持建立自由线程(free-threaded)的应用。这意味着多个线程可以访问同样一套的共享数据。本文的以下部分将讨论一下多线程的一些基本点。

问题

虽然VB6支持多个单线程的单元,不过它并不支持一个自由线程的模型,即不允许多个线程使用同一套数据。在许多的情况下,你需要建立一个新的线程来进行后台的处理,这样可提高应用的可用性,否则,一个长的处理就可以令程序的响应变得很慢,例如你按下表格上的一个取消按钮,却很久都没有响应。

解决办法

由于VB.NET使用了CLR(Common Language Runtime),从而拥有了许多的新特性,其中的一个是可以创建自由线程的应用。

使用线程

在VB.NET中,运用线程是很简单的。我们将在后面涉及其中的细节,现在我们首先来创建一个简单的表格,它使用一个新的线程来运行一个后台处理。第一件要做的事情是创建运行在新线程上的后台任务。以下的代码执行一个相当长的运行处理--一个无限的循环:

Private Sub BackgroundProcess()

Dim i As Integer = 1

Do While True

ListBox1.Items.Add("Iterations: " + i)

i += 1

Loop

End Sub

这段代码无限地循环,并且在每次执行时为表格上的一个列表框加入一个项目。如果你对VB.NET不熟悉的话,你将会发现这段代码和VB6的有一些区别:

. 在声明变量Dim i As Integer = 1时赋值

. 使用+=操作符i += 1代替i = i + 1

. 没有使用Call关键字

一旦我们拥有了一个工作的处理,我们就需要将这段代码分配给一个线程处理,并且启动它。为此我们要使用线程对象(Thread object),它是.NET架构类中System.Threading命名空间的一部分。在实例化一个新的线程类时,我们将要在线程类构造器执行的代码块的一个引用传送给它。以下的代码创建一个新的线程对象,并且将BackgroundProcess的一个引用传送给它:

Dim t As Thread

t = New Thread(AddressOf Me.BackgroundProcess)

t.Start()

AddressOf操作符创建了一个到BackgroundProcess方法的委派对象。在VB.NET中,一个委派是一个类型安全、面向对象的函数指针。在实例化该线程后,你可以通过调用线程的Start()方法来开始执行代码。

控制线程

在线程启动后,你可以通过线程对象的一个方法来控制它的状态。你可以通过调用Thread.Sleep方法来暂停一个线程的执行,这个方法可以接收一个整型值,用来决定线程休眠的时间。拿前面的例子来说,如果你想让列表项目增加的速度变慢,可以在其中放入一个sleep方法的调用:

Private Sub BackgroundProcess()

Dim i As Integer = 1

Do While True

ListBox1.Items.Add("Iterations: " + i)

i += 1

Thread.CurrentThread.Sleep(2000)

Loop

End Sub

CurrentThread是一个public static的属性值,可让你得到当前运行线程的一个引用。

你还可以通过调用Thread.Sleep (System.Threading.Timeout.Infinite)来让线程进入休眠状态,有点特别的是,这个调用的休眠时间是不确定的。要中断这个休眠,你可以调用Thread.Interrupt方法。

与休眠和中断类似的是挂起和恢复。挂起可让你暂停一个线程,直到另一个线程调用Thread.Resume为止。休眠和挂起的区别是,后者并不立刻让线程进入一个等待的状态,线程并不会挂起,直到.NET runtime认为现在已经是一个安全的地方来挂起它了,而休眠则会立刻让线程进入一个等待的状态。

最后要介绍的是Thread.Abort,它会停止一个线程的执行。在我们的那个简单例子中,如果要加入一个按钮来停止处理,很简单,我们只要调用Thread.Abort方法就行了,如下所示:

Private Sub Button2_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles Button2.Click

t.Abort()

End Sub

这就是多线程的强大之处。用户界面的响应很好,因为它运行在一个单独的线程中,而后台的处理运行在另外一个线程中。在用户按下取消按钮时,便会马上得到响应,并且停止处理。

上面的例子只是一个相当简单的应用。在编程时,你还需要使用到多线程的许多复杂特性。其中的一个问题是如何将程序的数据由线程类的构造器传入或者传出,也就是说,对于放到另外一个线程中的过程,你既不能传参数给它,也不能由它返回值。这是由于你传入到线程构造器的过程是不能拥有任何的参数或者返回值的。为了解决这个问题,可以将你的过程封装到一个类中,这样方法的参数就可使用类中的字段。

这里我们举一个简单的例子,如果我们要计算一个数的平方,即:

Function Square(ByVal Value As Double) As Double

Return Value * Value

End Function

为了在一个新的线程中使用这个过程,我们将它封装到一个类中:

Public Class SquareClass

Public Value As Double

Public Square As Double

Public Sub CalcSquare()

Square = Value * Value

End Sub

End Class

使用这些代码来在一个新的线程上启动CalcSquare过程,如下所示:

Private Sub Button1_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles Button1.Click

Dim oSquare As New SquareClass()

t = New Thread(AddressOf oSquare.CalcSquare)

oSquare.Value = 30

t.Start()

End Sub

要注意到,在线程启动后,我们并没有检查类中的square值,因为即使你调用了线程的start方法,也不能确保其中的方法马上执行完。要从另一个线程中得到值,有几个方法,这里使用的方法是最简单的,即是在线程完成的时候触发一个事件。我们将在后面的线程同步中讨论另一个方法。以下的代码为SquareClass加入了事件声明。

Public Class SquareClass

Public Value As Double

Public Square As Double

Public Event ThreadComplete(ByVal Square As Double)

Public Sub CalcSquare()

Square = Value * Value

RaiseEvent ThreadComplete(Square)

End Sub

End Class

在调用代码中捕捉事件的方法和VB6差不多,你仍然要声明WithEvents变量,并且在一个过程中处理事件。有些不同的是,你声明处理事件的过程使用的是Handles关键字,而不是通过VB6中通常使用的Object_Event。

Dim WithEvents oSquare As SquareClass

Private Sub Button1_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles Button1.Click

oSquare = New SquareClass()

t = New Thread(AddressOf oSquare.CalcSquare)

oSquare.Value = 30

t.Start()

End Sub

Sub SquareEventHandler(ByVal Square As Double) _

Handles oSquare.ThreadComplete

MsgBox("The square is " & Square)

End Sub

对于这种方法,要注意的是处理事件的过程,在这个例子中的是SquareEventHandler,将运行在产生该事件的线程中。它并不是运行在表格执行的线程中。

同步线程

在线程的同步方面,VB.NET提供了几个方法。在上面的平方例子中,你要与执行计算的线程同步,以便等待它执行完并且得到结果。另一个例子是,如果你在其它线程中排序一个数组,那么在使用该数组前,你必须等待该处理完成。为了进行这些同步,VB.NET提供了SyncLock声明和Thread.Join方法。

SyncLock可得到一个对象引用的唯一锁,只要将该对象传送给SyncLock就行了。通过得到这个唯一锁,你可以确保多个线程不会访问共享的数据或者在多个线程上执行的代码。要得到一个锁,可使用一个较为便利的对象--与每个类关联的System.Type对象。System.Type对象可通过使用GetType方法得到:

Public Sub CalcSquare()

SyncLock GetType(SquareClass)

Square = Value * Value

End SyncLock

End Sub

另一个是Thread.Join方法,它可让你等待一个特定的时间,直到一个线程完成。如果该线程在你指定的时间前完成了,Thread.Join将返回True,否则它返回False。在平方的例子中,如果你不想使用触发事件的方法,你可以调用Thread.Join的方法来决定计算是否完成了。代码如下所示:

Private Sub Button1_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles Button1.Click

Dim oSquare As New SquareClass()

t = New Thread(AddressOf oSquare.CalcSquare)

oSquare.Value = 30

t.Start()

If t.Join(500) Then

MsgBox(oSquare.Square)

End If

End Sub

对于这种方法,要注意的是处理事件的过程,在这个例子中的是SquareEventHandler,将运行在产生该事件的线程中。它并不是运行在表格执行的线程中。

相关内容

热门资讯

对话《望月》负责人:花了几个亿... 一家靠回合制与ARPG产品站稳脚跟的游戏中厂,要砸几个亿做一款中式都市开放世界,放到如今,都是一场足...
《恋与深空》怎么又惹“众怒”了... 早上9:59,我点开外卖软件,在心跳如鼓、摩拳擦掌的一分钟后——时间跳到10:00的那一刻,个40块...
王者荣耀S44新赛季首件超标装... 大家好,6月25日上午8点,正式服会提前开启S44新赛季。 其中,策划不仅会调整30多位英雄和8件装...
米哈游广州建公司,《如鸢》制作... 随着这些年国内游戏厂商声量壮大,中国游戏全球化/区域化新品布局、中国厂商于宣发联动、投资并购等业态频...
年过半百创业,双点工作室的创始... “我们整个夏天都排满了令人兴奋的公告,多到我们自己都有点混乱。” 编辑:Lushark 采访:强连虎...
原创 E... 在无畏契约2026伦敦大师赛败者组决赛中,EDG遭到LEV3-0零封,最终止步本次赛事,拿到第三名的...
原创 新... 太平洋时间6月23日上午11点/北京时间6月24日凌晨2点 (映维网Nweon 2026年06月...
乐高星战人仔的“无瞳孔”争议:... 乐高星球大战 2027 年 1 月传闻里,75470 穆斯塔法大对决必须单独拿出来聊。玩家盯着的可能...
原创 G... GTA6又要延期?官方发售日期突然被删除,引起玩家猜测,《GTA6》距离正式发售还有数月时间,但近日...
《毁灭战士》经典配乐创作者鲍比... IT之家 6 月 21 日消息,经典游戏《毁灭战士》标志性配乐的创作者鲍比 · 普林斯去世,享年 8...
我确信,吹哥确实造出了解谜游戏... 很难说,《沉星之序》是不是2026年最受期待的独立游戏。 单看开发者Jonathan Blow的名字...
55秒拆3塔拿到3K经济优势,... 欢迎来到最新一期【实战教学】 案例一 红色方的边野刚抓死了一蓝色方的对抗路,抓人过程不重要,重要...
德国玩家用了65000块乐高,... 德国玩家 Tommy Schmidt 用约 65000 块乐高积木,拼出了一根 2 米长的巨型香肠。...
原创 L... 各位LPL的观众和英雄联盟召唤师大家好,这里是天下游戏汇。 昨天ENC国家杯是公布了各个国家队的参赛...
曝LPL前登峰强队被挖空摆烂!... 大家好,英雄联盟S16赛季的比赛已经正式开始了,相信绝大多数的玩家都关注了最近一段时间的对抗,各大赛...
鹰角,难了 开卷考也不容易啊。 文/修理 二游玩家今年的第一次高考,来了。 昨天中午12点,《明日方舟:终末地》...
B站修改B萌规则大幅扩充参赛名... 在很久很久以前,B萌曾经是一个持续长达三年的定番活动——大家为喜欢的角色拉票,甚至成立群组商讨“战术...
职业比赛主动发起投降,晚星手速... KPL精彩赛事依旧在火热进行中,自从夏季赛开打以来,网友们看的那叫一个过瘾,毕竟好看的比赛太多了,更...
那个能对宝可梦为所欲为的游戏,... 几年前姬写过一篇皮卡丘能不能和别的宝可梦交配出船新品种的文章→ 虽然正传游戏里面不能,但同人创作还是...