Mam usługę w aplikacji, która pozwala mi przekazywać wiadomości z różnych źródeł, które zostaną umieszczone na prostej liście. Usługa, działająca we własnym wątku, będzie okresowo przetwarzać wszystkie wiadomości z listy na różne pliki; jeden plik dla każdego źródła, którym następnie zarządza się pod względem wielkości.Czy należy dwukrotnie sprawdzić przed i po zablokowaniu listy?
Moje pytanie dotyczy właściwego sprawdzania wiadomości i wykonywania blokady wokół kodu, który uzyskuje dostęp do listy. Są tylko dwa miejsca, które mają dostęp do listy; jeden to miejsce, w którym wiadomość jest dodawana do listy, a druga to miejsce, w którym wiadomości są zrzucane z listy na listę przetwarzania.
Dodawanie wiadomość do listy:
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
tworzenie listy:
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...
Moje pytania to: zrobić podwójne sprawdzanie kiedy jestem przetwarzanie listy, patrz niżej? I dlaczego? A może nie? Czy są jakieś niebezpieczeństwa związane z dostępem do własności licznika listy poza blokadą? Czy któraś z metod jest lepsza lub bardziej wydajna? I dlaczego? A może nie?
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...
Rozumiem, że w tym konkretnym przypadku, to może nie ma znaczenia, czy robię podwójną kontrolę Biorąc pod uwagę fakt, że poza funkcją przetwarzania, lista może tylko rosnąć. Ale to jest mój przykład pracy i staram się poznać dokładniejsze szczegóły wątków.
Z góry dziękuję za wszelkie spostrzeżenia ...
Po kilku dalszych badań, dziękuję „na coon”, a niektóre programy eksperymentalne, mam kilka dalszych myśli.
W odniesieniu do ReaderWriterLockSlim, mam następujący przykład, który wydaje się działać dobrze. Pozwala mi to odczytać liczbę wiadomości na liście bez ingerencji w inny kod, który może próbować odczytać liczbę wiadomości z listy lub same wiadomości. A gdy chcę przetworzyć listę, mogę uaktualnić tryb blokady do zapisu, zrzucić wiadomości na listę przetwarzania i przetworzyć je poza blokadami odczytu/zapisu, nie blokując żadnych innych wątków, które mogą chcieć dodać lub przeczytać , więcej wiadomości.
Należy zauważyć, że w tym przykładzie użyto prostszej konstrukcji dla komunikatu, String, w przeciwieństwie do poprzedniego przykładu, w którym użyto typu wraz z kilkoma innymi metadanymi.
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
Jedyny problem, jaki widzę przy użyciu tej techniki, to to, że deweloper musi być sumienny w kwestii pozyskiwania i zwalniania zamków. W przeciwnym razie wystąpią zakleszczenia.
Jeśli chodzi o to, czy jest to bardziej efektywne niż użycie SyncLock, naprawdę nie mogłem powiedzieć. W tym konkretnym przykładzie i jego zastosowaniu uważam, że albo wystarczy. Nie zrobiłbym podwójnego sprawdzenia z tych samych powodów, dla których "Coon" dał o przeczytaniu zliczenia, podczas gdy ktoś inny to zmienia. Biorąc pod uwagę ten przykład, SyncLock zapewnia taką samą funkcjonalność. Jednak w nieco bardziej złożonym systemie, w którym wiele źródeł może odczytywać i zapisywać na liście, idealne byłoby urządzenie ReaderWriterLockSlim.
W przypadku listy BlockingCollection następujący przykład działa jak wyżej.
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
sama prostota ...
Jaką kontrolę masz nad architekturą usługi? Wydaje się, że wiele ciężkich uniesień można było zrobić przy użyciu MSMQ. http://msdn.microsoft.com/en-us/library/ms789048.aspx – Trevor
Pozwolę sobie wyjaśnić, kiedy mówię o usłudze, mam na myśli klasę wewnętrzną aplikacji, która jest uruchamiana na początku aplikacji, tworzy wątek do okresowego przetwarzania komunikatów nadanych mu przez inne części aplikacji, a nie usługę Windows. Uogólniona obsługa komunikatów, jeśli zajdzie taka potrzeba, zamknięta całkowicie w aplikacji, działająca na własnym wątku tła. Ta "usługa" ma za zadanie 1) zapewnienie prostego sposobu na tworzenie wiadomości oraz 2) zapobieganie przechowywaniu wiadomości w zbyt małym miejscu. Nie ma również potrzeby przesyłania komunikatów między aplikacjami. –
Wiadomość może być niewłaściwą terminologią dla tej aplikacji. To nie tyle wiadomość, co rejestrowane zdarzenie. Zdarzenie "debugowania", "komunikatu", "ostrzeżenia" lub "błędu", które jest specyficzne i istotne tylko dla aplikacji lub modułu, który utworzył wiadomość. –