Static Property and Lock

Is this code correct or is there any possibility of random interdependent interdependencies, etc.?

Is it possible to use static properties and block together? Or is a static property thread safe?

Private Shared _CompiledRegExes As List(Of Regex) Private Shared Regexes() As String = {"test1.Regex", "test2.Regex"} Private Shared RegExSetupLock As New Object Private Shared ReadOnly Property CompiledRegExes() As List(Of Regex) Get If _CompiledRegExes Is Nothing Then SyncLock RegExSetupLock If _CompiledRegExes Is Nothing Then _CompiledRegExes = New List(Of Regex)(Regexes.Length - 1) For Each exp As String In Parser.Regexes _CompiledRegExes.Add(New Regex(exp, RegexOptions.Compiled Or RegexOptions.CultureInvariant Or RegexOptions.IgnoreCase)) Next End If End SyncLock End If Return _CompiledRegExes End Get End Property 

If this is not obvious code, it compiles the regular expressions and stores them in List (Of Regex) so that they can work faster. And it shares, so every instance of a class can benefit from it.

+4
source share
6 answers

Your code is not thread safe because you use a double-control lock without making the field unstable. Unfortunately, VB.NET does not have a mutable modifier, so you cannot apply a normal fix. Just acquire a lock every time instead, or use static initialization to initialize _CompiledRegExes when the type is initialized.

See my one-page page for a general discussion of this issue with respect to single cells - I know this is not exactly a singleton, but it is Close. The page gives C # code, but it should be pretty easy to understand.

In addition, I usually recommend doing read-only lock variables. You really don't want to change the value :)

In outline:

  • No, static properties are not thread safe automatically
  • Yes, it's okay to block a static variable (but initialize it explicitly, just like you)
  • Do not try to write lock code or with a low lock level if you really do not know what you are doing. I consider myself knowledgeable enough about threads, and I'm still not trying to use double check locking, etc.
  • Type initialization is thread safe (with a few caveats, if you have complex initializers that end up referencing each other) to have a good initialization like this - then you really don't need a lock.

EDIT: You do not need to specify a singleton type. Just write a function to initialize the list and return it, then use this function in the initializer for the variable:

 ' This has to be declared *before* _CompiledRegExes ' ' as the initializer will execute in textual order ' ' Alternatively, just create the array inside BuildRegExes ' ' and don't have it as a field at all. Unless you need the array ' ' elsewhere, that would be a better idea. ' Private Shared ReadOnly Regexes() As String = {"test1.Regex", "test2.Regex"} Private Shared ReadOnly _CompiledRegExes As List(Of Regex) = BuildRegExes() Private Shared ReadOnly Property CompiledRegExes() As List(Of Regex) Get Return _CompiledRegExes End Get End Property Private Shared Function BuildRegExes() As List(Of Regex) Dim list = New List(Of Regex)(Regexes.Length - 1) For Each exp As String In Regexes _CompiledRegExes.Add(New Regex(exp, RegexOptions.Compiled Or RegexOptions.CultureInvariant Or RegexOptions.IgnoreCase)) Next Return list End Function 
+8
source

(EDIT by jonskeet). This text is intended to be formatted correctly for the start of the class. I don't know why this fails if there is no text before the code.)

 Class Better Private Shared Regexes() As String = {"test1.Regex", "test2.Regex"} Private Shared _CompiledRegExes As List(Of Regex) = BuildList(Regexes) Private Shared Function BuildList(ByVal source() As String) As List(Of Regex) Dim result = New List(Of Regex)(Regexes.Length - 1) For Each exp As String In source result.Add(New Regex(exp, RegexOptions.Compiled Or RegexOptions.CultureInvariant Or RegexOptions.IgnoreCase)) Next Return result End Function Public Shared ReadOnly Property _CompiledRegExes1() As List(Of Regex) Get Return _CompiledRegExes End Get End Property End Class Class MoreBetter Private Shared ReadOnly Regexes() As String Private Shared ReadOnly _CompiledRegExes As List(Of Regex) Shared Sub New() Regexes = New String() {"test1.Regex", "test2.Regex"} _CompiledRegExes = New List(Of Regex)(Regexes.Length - 1) For Each exp As String In Regexes _CompiledRegExes.Add(New Regex(exp, RegexOptions.Compiled Or RegexOptions.CultureInvariant Or RegexOptions.IgnoreCase)) Next End Sub Public Shared ReadOnly Property _CompiledRegExes1() As List(Of Regex) Get Return _CompiledRegExes End Get End Property End Class 

EDIT: The reason I like the second is because it is less error prone. The first will not work if "Private Shared _CompiledRegExes" is accidentally moved before "Private Shared Regexes ()"

+2
source

VB.Net has a Static keyword that you can use to declare variables in elements or properties. Variables declared in this way retain their state in all method calls and are guaranteed to be thread safe (implemented with the Monitor class behind the scenes).

+1
source

Although I rarely support the use of this method, it is legal.

 Class UsingStatic Private Shared Regexes() As String = {"test1.Regex", "test2.Regex"} Public Shared ReadOnly Property CompiledRegExes() As List(Of Regex) Get Static _CompiledRegExes As List(Of Regex) = BuildList(Regexes) <--executes once Return _CompiledRegExes <-- executes every time it is called End Get End Property Private Shared Function BuildList(ByVal source() As String) As List(Of Regex) Dim result = New List(Of Regex)(Regexes.Length - 1) For Each exp As String In source result.Add(New Regex(exp, RegexOptions.Compiled Or RegexOptions.CultureInvariant Or RegexOptions.IgnoreCase)) Next Return result End Function End Class 

Note that you MUST put the constructor for _CompiledRegExes on the same line. This tiny change will make you get a new object every time.

 Public Shared ReadOnly Property CompiledRegExes() As List(Of Regex) Get Static _CompiledRegExes As List(Of Regex) <--executes once _CompiledRegExes = BuildList(Regexes) <-- executes every time it is called Return _CompiledRegExes End Get End Property 
+1
source

Dead ends do not happen when you have only one resource.

As for static properties, they are not thread safe automatically. This is a general approach for locking a private static field.

0
source

from msdn "A lockobject expression must always be evaluated by an object that belongs exclusively to your class. You must declare a Private object variable to protect data belonging to the current instance, or a Private Shared object variable to protect data common to all instances."

0
source

All Articles