一般来说,你希望在独立的线程中运行各种处理,而不需要访问共享的资源。建议的方法如下:
1、封装要运行的处理到一个类中,并且留一个入口来启动该处理,例如Public Sub Start()并且初始化变量来处理状态
2、创建一个独立的类实例
3、设置处理需要的实例变量
4、在一个独立的线程中调用入口
5、不要引用该类的实例变量
只要使用这个方法,全部的实例变量对于线程都是“私有的”,因此可以无需担心同步的问题。
不过,有时这种情况是不能避免的,例如数据库连接或者文件处理。为了确保某线程在访问这些资源时其它线程处于等待状态,你可以使用Monitor类和它的相关方法,包括有Enter, Exit, TryEnter, Wait, Pulse和PulseAll。
例如,假定上面代码中的Instructors类包含了一个类级的SqlConnection对象,该对象被所有的方法共享,并且用来连接数据库。这就是一个资源共享的例子,它被类中的所有方法所共享。
注意:
虽然使用连接池可提供一个更富扩展性的方案,不过这个例子满足我们当前的需要,它让所有的数据库访问通过一个单一的数据库连接进行。这种方式对于需要一个持久的数据库连接的应用是适合的,不过不适合用在分布式的应用。
这个例子中,我们假设在调用GetPhotos后,客户端继续调用一个使用该连接对象的方法。由于连接可能正在被GetPhotos使用,如果SqlConnection正在忙于处理其它的结果,该方法将会抛出一个例外。
要避免这种情形,GetPhotos方法可以使用Monitor的共享方法在其代码中创建critical section。简单说来,critical section就是调用Monitor类的Enter和Exit方法所构成的代码块,通过它,访问的同步是基于传送至Enter方法的对象。也就是说,如果GetPhotos方法要独立地使用SqlConnection,它必须要创建一个critical section,在该section的开始部分,通过传送SqlConnection到Monitor的Enter方法中,并且在结束的时候调用Exit方法。被传送的对象可以是任何继承System.Object的对象。
如果该对象正在被其它的线程使用,Enter方法将会阻塞直到对象被释放。你也可以调用TryEnter方法,该方法不会阻塞,它只会返回一个布尔值指示该对象是否在使用中。一旦进入critical section,GetPhotos方法可以使用SqlConnection执行一个存储过程,并且将结果写出来。在关闭结果集SqlDataReader后,就会调用Monitor类的Pulse方法,以通知等待队列中的下个线程该对象已经释放了。然后就会将线程移动到ready队列中,以便准备开始处理。PulseAll方法则通知全部的等待线程该对象准备被释放。最后就会调用Exit,从而释放monitor并且结束critical section部分。这部分代码的框架见下。
同步的资源。以下的例子展示了GetPhotos方法将使用Monitor类来确保两个线程不会同时使用SqlConnection对象
Public Sub GetPhotos()
Dim cmSQL As SqlCommand
Dim sdrIns As SqlDataReader
Try
' Execute proc
cmSQL = New SqlCommand("usp_GetPhotos", mcnSQL)
cmSQL.CommandType = CommandType.StoredProcedure
' Enter critical section
Monitor.Enter(mcnSQL)
' Alternate code
' Do While Not Monitor.TryEnter(mcnSQL)
' Thread.CurrentThread.Sleep(100)
' Loop
sdrIns = cmSQL.ExecuteReader()
Catch e As Exception
End Try
Do While sdrIns.Read
' Read the data and write it to a binary stream
Loop
sdrIns.Close
Monitor.Pulse(mcnSQL)
Monitor.Exit(mcnSQL)
' Exited critical section
Return
End Sub
很明显,critical sections仅应该在需要的时候创建,因为它们会阻塞线程,从而会影响整体的吞吐量。
上一页 [1] [2] [3] [4] [5] [6] [7] 下一页