Должен ли я дважды проверять до и после блокировки списка?

У меня есть служба приложений -, которая позволяет мне передавать сообщения из различных источников, которые будут помещены в простой список. Служба, работающая в собственном потоке, будет периодически обрабатывать все сообщения в списке в различные файлы; один файл для каждого источника, размер которого затем регулируется.

Мой вопрос касается правильного способа проверки сообщений и выполнения блокировки кода, который обращается к списку. Есть только два места, которые имеют доступ к списку; в одном случае сообщение добавляется в список, а в другом сообщения выгружаются из списка в список обработки.

Добавление сообщения в список:

Public Sub WriteMessage(ByVal messageProvider As IEventLogMessageProvider, ByVal logLevel As EventLogLevel, ByVal message As String)
    SyncLock _SyncLockObject
        _LogMessages.Add(New EventLogMessage(messageProvider, logLevel, Now, message))
    End SyncLock
End Sub

Обработка списка:

Dim localList As New List(Of EventLogMessage)
SyncLock _SyncLockObject
    If (_LogMessages.Count > 0) Then
        localList.AddRange(_LogMessages)
        _LogMessages.Clear()
    End If
End SyncLock

' process list into files...

Мои вопросы: :должен ли я делать двойную проверку при обработке списка, см. ниже? И почему? Или почему нет? И есть ли опасность при доступе к свойству count списка вне блокировки? Какой из методов лучше или эффективнее? И почему? Или почему нет?

Dim localList As New List(Of EventLogMessage)
If (_LogMessages.Count > 0) Then
    SyncLock _SyncLockObject
        If (_LogMessages.Count > 0) Then
            localList.AddRange(_LogMessages)
            _LogMessages.Clear()
        End If
    End SyncLock
End If

' process list into files...

Я понимаю, что в данном конкретном случае может не иметь значения, если я сделаю двойную проверку, учитывая тот факт, что вне функции обработки список может только расти. Но это мой рабочий пример, и я пытаюсь изучить более тонкие детали многопоточности.

Заранее благодарим вас за любые идеи…


После некоторых дальнейших исследований, спасибо "Енот", и некоторого экспериментального программирования, у меня есть некоторые дополнительные мысли.

Что касается ReaderWriterLockSlim , у меня есть следующий пример, который работает нормально. Это позволяет мне читать количество сообщений в списке, не мешая другому коду, который может пытаться прочитать количество сообщений в списке или сами сообщения.И когда я хочу обработать список, я могу обновить свою блокировку до режима записи, сбросить сообщения в список обработки и обработать их вне каких-либо блокировок чтения/записи, таким образом, не блокируя любые другие потоки, которые могут захотеть добавить или прочитать, больше сообщений.

Обратите внимание, что в этом примере используется более простая конструкция для сообщения, строка, в отличие от предыдущего примера, в котором использовался тип вместе с некоторыми другими метаданными.

Private _ReadWriteLock As New Threading.ReaderWriterLockSlim()

Private Sub Process()
    ' create local processing list
    Dim processList As New List(Of String)
    Try
        ' enter read lock mode
        _ReadWriteLock.EnterUpgradeableReadLock()
        ' if there are any messages in the 'global' list
        ' then dump them into the local processing list
        If (_Messages.Count > 0) Then
            Try
                ' upgrade to a write lock to prevent others from writing to
                ' the 'global' list while this reads and clears the 'global' list
                _ReadWriteLock.EnterWriteLock()
                processList.AddRange(_Messages)
                _Messages.Clear()
            Finally
                ' alway release the write lock
                _ReadWriteLock.ExitWriteLock()
            End Try
        End If
    Finally
        ' always release the read lock
        _ReadWriteLock.ExitUpgradeableReadLock()
    End Try
    ' if any messages were dumped into the local processing list, process them
    If (processList.Count > 0) Then
        ProcessMessages(processList)
    End If
End Sub

Private Sub AddMessage(ByVal message As String)
    Try
        ' enter write lock mode
        _ReadWriteLock.EnterWriteLock()
        _Messages.Add(message)
    Finally
        ' always release the write lock
        _ReadWriteLock.ExitWriteLock()
    End Try
End Sub

Единственная проблема, которую я вижу в этом методе, заключается в том, что разработчик должен усердно устанавливать и снимать блокировки. В противном случае возникнут взаимоблокировки.

Что касается того, является ли это более эффективным, чем использование SyncLock , я действительно не могу сказать. Я считаю, что для этого конкретного примера и его использования будет достаточно любого из них. Я бы не стал делать двойную проверку по тем же причинам, которые привел «енот» о чтении счета, пока кто-то другой его меняет. В этом примере SyncLock обеспечивает ту же функциональность. Однако в немного более сложной системе, где несколько источников могут читать и записывать в список, ReaderWriterLockSlim был бы идеальным.


Что касается списка BlockingCollection , следующий пример работает так же, как и приведенный выше.

Private _Messages As New System.Collections.Concurrent.BlockingCollection(Of String)

Private Sub Process()
    ' process each message in the list
    For Each item In _Messages
        ProcessMessage(_Messages.Take())
    Next
End Sub

Private Sub AddMessage(ByVal message As String)
    ' add a message to the 'global' list
    _Messages.Add(message)
End Sub

Сама простота…

5
задан ThiefMaster 8 July 2012 в 09:18
поделиться