这个简单的VB.Net类线程安全吗?如果没有,我该如何改进呢?

use*_*208 3 vb.net thread-safety

Option Strict On

Public Class UtilityClass
  Private Shared _MyVar As String

  Public Shared ReadOnly Property MyVar() As String
    Get
      If String.IsNullOrEmpty(_MyVar) Then
        _MyVar = System.Guid.NewGuid.ToString()
      End If
      Return _MyVar
    End Get
  End Property

  Public Shared Sub SaveValue(ByVal newValue As String)
    _MyVar = newValue
  End Sub

End Class
Run Code Online (Sandbox Code Playgroud)

sup*_*cat 5

虽然锁定是添加线程安全性的一种很好的通用方法,但在涉及一次写入准不可变性的许多场景中,只要向其写入非空值,字段就应该变为不可变,Threading.Interlocked.CompareExchange可能会更好.本质上,该方法读取一个字段,并且 - 在其他人可以触摸它之前 - 当且仅当该字段与提供的"compare"值匹配时才写入新值; 它返回在任何情况下读取的值.如果两个线程同时尝试a CompareExchange,两个线程都将字段的当前值指定为"compare"值,则其中一个操作将更新该值而另一个操作将不会,并且每个操作将"知道"它是否成功.

CompareExchange有两种主要的使用模式.第一个对于生成可变单例对象最有用,其中每个人都看到同一个实例很重要.

If _thing is Nothing then
    Dim NewThing as New Thingie() ' Or construct it somehow
    Threading.Interlocked.CompareExchange(_thing, NewThing, Nothing)
End If
Run Code Online (Sandbox Code Playgroud)

这种模式可能就是你所追求的.请注意,如果一个线程在另一个线程完成的时间和它执行的时间之间输入上面的代码CompareExchange,则两个线程可能最终创建一个新的Thingie.如果发生这种情况,首先到达CompareExchange的线程将使其新实例存储在_thing中,而另一个线程将放弃其实例.在这种情况下,线程不关心它们是赢还是输; _thing将在其中包含一个新实例,并且所有线程都将在那里看到相同的实例.还要注意,因为在第一次读取之前没有内存屏障,理论上有可能在过去的某个时间检查过_thing的值的线程可能会继续看到它Nothing直到某些东西导致它更新其缓存,但如果发生这种情况,唯一的结果是创建一个无用的新实例,Thingie然后在已经写入的Interlocked.CompareExchange查找时将被丢弃_thing.

另一个主要用法模式对于更新对不可变对象的引用很有用,或者 - 稍作调整 - 更新某些值类型,如Integer或Long.

Dim NewThing, WasThing As Thingie
Do
    WasThing = _thing
    NewThing = WasThing.WithSomeChange();
Loop While Threading.Interlocked.CompareExchange(_thing, NewThing, WasThing) IsNot WasThing
Run Code Online (Sandbox Code Playgroud)

在这种情况下,假设存在一些方法,在给定对Thingie的引用的情况下,可以廉价地生成以某种期望的方式不同的新实例,可以以线程安全的方式对_thing执行任何此类操作.例如,给定a String,可以容易地产生String具有附加的一些字符的新的.如果有人希望以线程安全的方式将某些文本附加到字符串中(这样如果一个线程尝试添加Fred而另一个尝试添加Joe,则最终结果将是追加FredJoe或者JoeFred,而不是类似FrJoeed),以上代码将读取每个线程_thing,生成其文本附加的版本,并尝试更新_thing.如果某个其他线程_thing在平均时间内更新,则放弃构造的最后一个字符串,根据更新的内容创建一个新字符串_thing,然后重试.

请注意,虽然这种方法不一定比锁定方法更快,但它确实提供了一个优势:如果获取锁的线程陷入无限循环或者其他方式,则所有线程将永远被阻止访问锁定资源.相反,如果上述WithSomeChanges()方法陷入无限循环,其他用户_thing将不会受到影响.