Статическое свойство и блокировка использования - PullRequest
4 голосов
/ 27 марта 2009

Правильно ли указан этот код или есть вероятность случайных блокировок многопоточности и т. Д.?

Хорошо ли использовать статические свойства и блокировку вместе? Или статическое свойство в любом случае является потокобезопасным?

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

Если не очевидно, что код компилирует регулярные выражения и сохраняет их в List (Of Regex), чтобы они могли работать быстрее. И он является общим, так что каждый экземпляр класса может извлечь из него пользу.

Ответы [ 6 ]

8 голосов
/ 27 марта 2009

Ваш код не является потокобезопасным, поскольку вы используете двойную проверку блокировки, не делая поле нестабильным. К сожалению, в VB.NET нет модификатора volatile, поэтому вы не можете применить обычное исправление. Вместо этого просто получайте блокировку каждый раз или используйте статическую инициализацию для инициализации _CompiledRegExes при инициализации типа.

См. мою синглтон-страницу для общего обсуждения этого вопроса в отношении синглетонов - я знаю, что это не вполне синглтон, но это близко. На странице представлен код C #, но он должен быть довольно простым для понимания.

Кроме того, я обычно рекомендую делать переменные блокировки доступными только для чтения. Вы действительно не хотите менять значение:)

В общих чертах:

  • Нет, статические свойства не являются потокобезопасными автоматически
  • Да, можно блокировать статическую переменную (но инициализировать ее явно, как вы делаете)
  • Не пытайтесь писать код без блокировки или с низким уровнем блокировки, если вы действительно не знаете, что делаете. Я считаю себя достаточно осведомленным о потоках, и я все еще не пытаюсь использовать двойную проверку блокировки и т. Д.
  • Инициализация типа является поточно-ориентированной (с несколькими оговорками, если у вас есть сложные инициализаторы, которые в конечном итоге ссылаются друг на друга), так что сейчас подходящее время для инициализации, как это - тогда вам действительно не нужно замок.

РЕДАКТИРОВАТЬ: Вам не нужно делать тип синглтон. Просто напишите функцию, чтобы инициализировать список и вернуть его, затем используйте эту функцию в инициализаторе для переменной:

' 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
2 голосов
/ 27 марта 2009

(РЕДАКТИРОВАТЬ Jonskeet) Этот текст только для того, чтобы получить правильное форматирование для начала класса. Понятия не имею, почему происходит сбой, если перед кодом нет текста.)

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

РЕДАКТИРОВАТЬ: Мне нравится второй, потому что он менее подвержен ошибкам. Первый вариант не будет работать, если «Private Shared _CompiledRegExes» случайно перемещен перед «Private Shared Regexes ()»

1 голос
/ 28 марта 2009

Хотя я редко поддерживаю использование этого метода, это законно.

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

Обратите внимание, что вы ДОЛЖНЫ поместить конструктор для _CompiledRegExes в одну строку. Это крошечное изменение заставит вас каждый раз получать новый объект.

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 голос
/ 27 марта 2009

VB.Net имеет ключевое слово Static, которое можно использовать для объявления переменных в элементах или свойствах. Переменные, объявленные таким образом, сохраняют свое состояние при вызовах метода и гарантированно являются потокобезопасными (реализовано с классом Monitor за кулисами).

0 голосов
/ 28 марта 2009

from msdn "Выражение lockobject всегда должно оцениваться как объект, принадлежащий исключительно вашему классу. Вы должны объявить переменную Private object для защиты данных, принадлежащих текущему экземпляру, или переменную Private Shared object для защиты данных, общих для всех экземпляры ".

0 голосов
/ 27 марта 2009

Блокировки не возникают, когда у вас есть только один ресурс.

Что касается статических свойств, то они не являются потокобезопасными автоматически. Это обычный подход для блокировки частного статического поля.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...