[NET]在你的VB.NET应用程序中使用多线程

时间:2021-12-07 13:58:09
在你的VB.NET应用程序中使用多线程
   

在你的VB.NET应用程序中使用多线程


   很长时间以来,开发人员一直要求微软为VB增加更多的线程功能——这一点在VB.NET中终于实现了。VB6不支持创建多线程的EXEDLL以及OCX。但这种措词容易引起误解,这是因为VB6支持执行多个单线程的单元。一个单元实际上是代码执行的场所而且单元的边界限制了外部代码对单元内部的访问。


   VB.NET支持创建*线程的应用程序。这意味着多个线程可以访问同一个共享的数据集。本文将带领你了解多线程的基本内容。


虽然VB支持多个单线程的单元,但并不支持允许多个线程在同一个数据集上运行的*线程模型。在很多情况下,产生一个运行后台处理程序的新线程会提高应用程序的可用性。一种很显然的情况就是当执行一个可能使窗体看起来停止响应的长过程时,你一定会想在窗体上放置一个取消按钮。


解决方法


由于VB.NET使用公共语言运行时(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中无法完成的事:


l          在声明变量时对其赋值 Dim i As Integer=1


l          使用+=操作符 i+=1代替了i=i+1


l          Call关键字已经被去除了


一旦我们有了一个工作过程,便需要将这段代码指派给一个新的线程并开始它的执行。完成这项工作,我们需要使用Thread对象,它是.NET框架类中System.Threading命名空间的一部分。当实例化了一个新的Thread类时,我们向其传递一个引用,这个引用指向我们想要在Thread类的构造函数中执行的代码块。下面的代码创建一个新的Thread对象并将指向BackgroundProcess的引用传递给它:


Dim t As Thread


t = New Thread(AddressOf Me.BackgroundProcess)


t.Start()


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


使线程处于控制之下


当线程开始之后,你可以通过使用Thread对象的方法对其状态进行一定的控制。你可以通过调用Thread.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是一个公共静态属性,它可以使你获取一个对当前运行线程的引用。


你还可以通过调用Thread.Sleep (System.Threading.Timeout.Infinite)使一个线程处于一种时间不确定的休眠状态。要中断这种休眠,可以调用Thread.Interrupt 方法。


类似与SleepInterrupt的是SuspendResumeSuspend允许你阻塞一个线程直到另外的线程调用Thread.ResumeSleepSuspend之间的区别在于后者不是立即使一个线程处于等待状态。在.NET运行时确定线程是处于一个安全的挂起位置之前,线程是不会挂起的。Sleep则是立即使线程进入等待状态。


最后,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


在此便可以看出多线程的能力。用户界面看起来对用户是有响应的,因为它运行在一个线程中而后台的处理程序运行在另一个线程中。取消按钮会立即响应用户的click事件同时处理过程被中止。


通过多线程的过程传递数据


上一个例子展示了一种相当简单的情况。在你编程的时候,多线程有很多需要解决的复杂问题。你将会遇到的一个问题是向传递给Thread类构造函数的过程传递数据以及从这个过程传出数据。换言之,你想要在另一个线程上开始的过程不能接收任何参数而且你也不能从这个过程返回任何数据。这是因为传递给线程构造函数的过程不能有任何参数或返回值。为了避开这个问题,将你的过程包装到一个类中,在这个类中此方法的参数被表示成类的一个域。


有一个简单的例子,如果我们有一个计算一个数的平方的过程:


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


注意当线程开始后,我们没有检查类的平方值,因为并不能保证一旦你调用线程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关键字声明处理事件的过程并且不再使用像VB6Object_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包含了一些语句用于提供线程的同步。在Square的例子中,你可能想同步执行计算的线程以便等到计算完成,这样便可以获得结果。举另外一个例子,如果你在一个单独的线程中对数组进行排序并且在使用这个数组之前要等待这个处理过程结束。为了实现这些同步,VB.NET提供了SyncLock语句和Thread.Join方法。


SyncLock获取了对传递给它的对象引用的独占性锁。通过取得这种独占锁,你可以确保多个线程不会访问共享的数据或是在多个线程上执行代码。一个可以方便地用于获取锁地对象是关联于每个类的System.Type对象。可以通过GetType方法获得System.Type对象:


Public Sub CalcSquare()


    SyncLock GetType(SquareClass)


        Square = Value * Value


    End SyncLock


End Sub


最后,Thread.Join方法允许你等待一段特定的时间直到一个线程结束。如果线程在你所确定的时间之前完成,Thread.Join返回True,否则的话返回False。在Square的例子中,如果我们不想引发事件,可以调用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


 


作者:Matthew Arnheiter是位于哈肯萨克市(Hackensack)GoAmerica Communications公司的一名资深顾问,他还是The Visual Basic Developer%314%159%26s Guide to Design Patterns and UML (Sybex, 2000)一书的作者。可以通过(201)996-1717 x2367Marnheitergoamerica.net与他取得联系。

posted @ 2005-10-19 00:17 guote 阅读(173) | 评论 (0)编辑  收藏
 

 





VB.NET(Visual Basic.NET)是为适应Microsoft .NET框架的需要,对Visual Basic进行了重大改造后的开发工具。它比Visual Basic 6.0功能更强大,更易于使用。其中最重要的变化就是对象继承,在VB.NET中,所有可管理的类型都衍生自System.Object。作为编程工具,最重要的一个特性就是垃圾碎片的回收,它由CLR(Common Language Runtime)进行控制,并提供更好的内存管理功能。通用的类型定义可以提供更好的互操作性和协同工作能力,因此,VB.NET显得更强大、更具可靠性。
在VB.NET中,大多数CLR内建的类型都在System名字空间里已经定义了。比如:System.Object, System.Int32, 和 System.String。要注意的是:一个名字空间可能嵌在另一个名字空间内,象System.Data里就有System.Data.DataSet的类。
代表是CLR编程模型很重要的一个新概念。代表是一个特殊类型的可管理的类,当你创建一个代表的实例时,你必须提供一个带有匹配签名的方法执行的地址,一旦创建了一个代表的实例,调用方法将变的很容易。
过去,我们利用VB开发多线程的应用程序时,是一件很令人痛苦的事,经常是多线程的程序运行是会变成多错误的程序!但在VB.NET中,这种状况已经大为改观。现在,我们利用VB.NET处理多线程和利用JAVA处理多线程一样简单了。下面我们就举个例子,来看看VB.NET的多线程吧!
下面是多线程程序threadtest.vb的代码:
 
imports System
imports System.Threading
 
public class AClass
 
public sub Method1()
Dim i as integer
For i = 1 to 100
Console.Writeline("这是类AClass方法method1的内容",i)
next
end sub
 
public sub Method2()
Dim i as integer
For i = 1 to 100
Console.Writeline("这是类AClass方法method2的内容",i)
next
end sub
 
end class
 
public class ThreadTest
 
public shared sub Main()
dim obj as new aclass
dim th1,th2 as thread
 
th1=new Thread(new ThreadStart(addressof obj.method1))
th1.start
 
th2=new Thread(new ThreadStart(addressof obj.method2))
th2.start
 
dim i as integer
For i= 1 to 100
Console.WriteLine("Main方法里的内容",i)
Next
end sub
 
end class
 
现在,来让我们剖析一下上面的例子:
1.我们创建了我们自己的类AClass,并创建了两个方法:Method1和Method2。
2.这两个方法很简单,只有一个For循环,向输出设备输出一些信息。
3.我们还定义了另外一个类ThreadTest来使用上面创建的类AClass。
4.在Main()方法中,我们创建了类Thread的实例。
5.类Thread可以在System.Threading名字空间里得到,它定义了处理线程的属性和方法。
6.在类Thread的构造器中,我们使用了类ThreadStart,类ThreadStart是一个代表,标志着当一个线程开始时就开始执行定义的方法。
7.为了执行定义的方法,我们实际调用的是线程的Start()方法。
8用VBC来编译上面的程序:
vbc /out:threadtest.exe threadtest.vb
9.运行编译后的程序,我们将会看到我们定义的两个方法和main()方法的混合输出,这就说明每一个方法都在自己的线程下运行。
10.除了上面的方法,线程还有下面常用的方法:
Stop():停止线程的运行。
Suspend():暂停线程的运行。
Resume():继续线程的运行。
Sleep():停止线程一段时间(单位为毫秒)。
 
上面只是VB.NET多线程的一个简单的例子,希望对大家有所启发!