Tengo un servicio en la aplicación que me permite alimentarlo con mensajes de varias fuentes, que se incluirán en una lista simple. El servicio, ejecutándose en su propio hilo, periódicamente procesará todos los mensajes de la lista en varios archivos; un archivo para cada fuente, que luego se gestionan por tamaño.¿Debo verificar dos veces antes y después de bloquear una lista?
Mi pregunta es sobre la forma correcta de buscar mensajes y realizar un bloqueo alrededor del código que accede a la lista. Solo hay dos lugares que acceden a la lista; uno es donde se agrega un mensaje a la lista y el otro es donde los mensajes se vuelcan de la lista a una lista de procesamiento.
Adición de un mensaje a la lista:
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
Procesamiento de la lista:
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...
Mis preguntas son: debo hacer un doble control cuando estoy procesando la lista, véase más adelante? ¿Y por qué? O por qué no? ¿Y hay algún peligro al acceder a la propiedad de recuento de la lista fuera del bloqueo? ¿Alguno de los métodos es mejor o más eficiente? ¿Y por qué? O por qué no?
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...
entiendo que en este caso particular, puede que no importe si hago un doble control dado el hecho de que, fuera de la función de procesamiento, la lista sólo puede crecer. Pero este es mi ejemplo de trabajo y estoy tratando de aprender sobre los detalles más finos de enhebrar.
Gracias de antemano por cualquier idea ...
Después de una investigación más profunda, gracias 'El Coon', y una programación experimental, tengo algunas ideas adicionales.
En cuanto al ReaderWriterLockSlim, tengo el siguiente ejemplo que parece funcionar bien. Me permite leer la cantidad de mensajes en la lista sin interferir con otros códigos que pueden estar tratando de leer la cantidad de mensajes en la lista, o los mensajes mismos. Y cuando deseo procesar la lista, puedo actualizar mi bloqueo al modo de escritura, volcar los mensajes en una lista de procesamiento y procesarlos fuera de cualquier bloqueo de lectura/escritura, sin bloquear así ningún otro hilo que pueda querer agregar o leer , más mensajes.
Tenga en cuenta que este ejemplo utiliza una construcción más simple para el mensaje, una Cadena, a diferencia del ejemplo anterior que utilizó un Tipo junto con algunos otros metadatos.
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
El único problema que veo con esta técnica es que el desarrollador debe ser diligente en la adquisición y liberación de los bloqueos. De lo contrario, se producirán bloqueos.
En cuanto a si esto es más eficiente que usar un SyncLock, realmente no podría decirlo. Para este ejemplo en particular y su uso, creo que cualquiera sería suficiente. No haría el doble control por las mismas razones que 'el mapache' dio sobre leer el conteo mientras alguien más lo está cambiando. Dado este ejemplo, el SyncLock proporcionaría la misma funcionalidad. Sin embargo, en un sistema un poco más complejo, en el que múltiples fuentes podrían leer y escribir en la lista, el ReaderWriterLockSlim sería ideal.
cuanto a la lista BlockingCollection, el siguiente ejemplo funciona como la de arriba.
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
simplicidad en sí ...
¿Cuánto control tiene sobre la arquitectura del servicio? Esto parece que una gran cantidad de trabajo pesado se puede hacer mediante el uso de MSMQ. http://msdn.microsoft.com/en-us/library/ms789048.aspx – Trevor
Permítanme aclarar, cuando digo servicio, me refiero a una clase, interna a la aplicación, que se inicia al principio de la aplicación, crea un hilo para procesar periódicamente los mensajes que le dan otras partes de la aplicación, no un servicio de Windows. Un manejador de mensajes generalizado, si lo desea, encapsulado totalmente dentro de la aplicación, ejecutándose en su propio hilo de fondo. Este 'servicio' tiene la tarea de 1) proporcionar una forma simple de crear mensajes, y 2) evitar que el almacenamiento de los mensajes consuma demasiado espacio de almacenamiento. Además, no hay necesidad de mensajería entre aplicaciones. –
Mensaje puede ser la terminología incorrecta para esta aplicación. No es tanto un mensaje como un evento registrado. Un evento de registro de 'depuración', 'mensaje', 'advertencia' o 'error' que es específico y relevante únicamente para la aplicación o módulo que creó el mensaje. –