在学习多线程开发的时候,我们总是要处理访问同步的问题。SyncLock提供了一种给对象加锁同步的方法,可以确保同一个代码段不被多个线程同时执行。而在多线程访问中访问类的字段,仍然会出现读写不同步的问题。在需要让多线程,操作系统或硬件来访问某字段时,我们希望这些访问能够读取到字段最新的值,同时写到变量的操作能够立即实现。有人说,默认难道不是这样吗?CLR有时会对字段访问进行优化,比如他察觉到你的代码没有修改字段的值,就有可能在你访问字段时提供上次访问的缓存值。这能够提高程序的效率,但CLR对多线程环境的判断就没有那么准确了,也许这个线程没有改变字段的值,但另一个线程抢占了CPU并修改了它呢。C#对此情况提供了volatile关键字,声明字段的时候如果加上了volatile,那么对该字段的任何请求,包括读和写操作,都会立刻得到执行。将多个线程要访问的字段声明成volatile是非常有效的手段,但Visual Basic却没有提供这一机制。所以我们只能从读写上做文章:

方法一:VolatileRead和VolatileWrite

System.Threading.Thread提供了两个方法——VolatileRead和VolatileWrite。这两个方法就能确保对变量/字段的访问立即执行。他们对很多类型进行了重载,包括许多基本值类型,指针和Object类型等。我们挑其中一个看看:

Public Shared Function VolatileRead(ByRef address As Integer) As Integer
Public Shared Sub VolatileWrite(ByRef address As Integer, ByVal value As Integer)

我们看到名为address的变量都是ByRef传递的,因为ByVal在此没有意义(它本身就会对变量执行一次读取)。我们只需将这两个函数用在对变量读写的地方即可。比如:

a = Thread.VolatileRead(Me.MyField) '取代a = Me.MyField的写法
Thread.VolatileWrite(Me.MyField, b) '取代Me.MyField = b的写法

在需要将字段值传递给Windows或硬件进行读写时,最好确保每次访问都用VolatileRead或VolatileWrite方法实现。但有时候读写操作是发生在外部的,比如用ByRef传递了字段给某Windows组件等等,我们很难控制外部组件的行为,于是就只能用下面的方法。

方法二:MemoryBarrier

System.Threading.Thread.MemoryBarrier可以让内存立刻同步,即让所有缓存内容都写到主内存中。在执行此方法后,读取变量便可读取到变量内储存的真实值;而在写操作后执行此方法,就可以确保内容正确写入变量中。所以我们要做的就是:在读取前和写入后执行Thread.MemoryBarrier方法。

Thread.MemoryBarrier()
a = Me.MyField

Me.MyField = b
Thread.MemoryBarrier()

有了Thread的这两个方法,我们在多线程开发中就又多了一样利器,更好地解决了对字段访问的同步问题。