У меня есть служба приложений -, которая позволяет мне передавать сообщения из различных источников, которые будут помещены в простой список. Служба, работающая в собственном потоке, будет периодически обрабатывать все сообщения в списке в различные файлы; один файл для каждого источника, размер которого затем регулируется.
Мой вопрос касается правильного способа проверки сообщений и выполнения блокировки кода, который обращается к списку. Есть только два места, которые имеют доступ к списку; в одном случае сообщение добавляется в список, а в другом сообщения выгружаются из списка в список обработки.
Добавление сообщения в список:
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
Сама простота…