2009-03-06 12 views
5

Otra pregunta de sincronización ... Espero que no se enojen;)Sincronización para lectores múltiples, escritor único?

Supongamos el siguiente escenario: una estructura de datos central (muy grande, por lo que realmente no quiero que sea inmutable y copiarlo) cada vez que ocurre un cambio. Incluso no quiero mantener múltiples copias en la memoria), múltiples hilos de lectura que acceden a esa estructura de datos de solo lectura y una secuencia de escritura que mantiene la estructura de datos actualizada en segundo plano.

Actualmente sincronizo todos los accesos a la estructura de datos, que funciona muy bien (sin efectos de sincronización, sin interbloqueos). Lo que no me gusta de este enfoque es que la mayoría de las veces tengo muchos hilos de lectura activos y el hilo del escritor solo está activo de vez en cuando. Ahora es completamente innecesario que los hilos del lector esperen a que otros subprocesos del lector finalicen. Podrían acceder fácilmente a la estructura de datos en paralelo siempre que el hilo del escritor no esté escribiendo en ese momento.

¿Hay alguna manera agradable y elegante de resolver este tipo de situaciones?

EDIT: Muchas gracias por las respuestas y los enlaces! Permítanme agregar solo otra pregunta breve y relacionada: si el código ejecutado dentro de las secciones críticas del lector toma solo un tiempo muy corto (como solo una búsqueda en una tabla hash), ¿vale la pena considerar implementar una de las técnicas que describe o la serialización? efecto de las cerraduras no tan mal en este caso? La escalabilidad y el rendimiento son muy importantes. ¿Qué piensas?

EDIT 2: Acabo de examinar una implementación de un solo escritor/múltiples lectores - bloqueo y esta implementación utiliza un monitor para sincronizar algunos códigos en el método WaitToRead. ¿No causa esto el mismo efecto de serialización que quería evitar en primer lugar? (Aún suponiendo que el código que se sincronizará es corto y rápido)

Respuesta

9

hay una clase para ese propósito en RTL (sysutils): TMultiReadExclusiveWriteSynchroniser

Es muy fácil de usar. No necesita categorizar estrictamente sus hilos como lector o escritor. Simplemente llame a "BeginRead" o "BeginWrite" en una secuencia para iniciar una operación segura de subprocesos. Llame a "EndRead" o "EndWrite" para finalizar la operación.

+0

Tenga en cuenta que esa clase está fatalmente rota en algunas versiones de Delphi. Se corrigió a partir de Delphi 2005, quizás antes. –

1

Cada vez que el escritor desea acceder, encola los lectores entrantes (pídales que esperen en la condición), espere a que los lectores activos finalicen, escriba y cuando termine deje que el acceso de lectores en cola

2

El uso de un bloqueo Reader-Writer resolverá el problema. Varios lectores pueden acceder a una base de datos y un escritor obtiene un bloqueo una vez que todos los lectores han terminado de leer.

Sin embargo, esto podría provocar que el escritor nunca tenga acceso a la fuente ya que siempre hay nuevos lectores que desean tener acceso. Esto puede resolverse bloqueando a los lectores nuevos cuando un escritor quiere acceder: el escritor tiene una prioridad mayor. El escritor obtiene acceso una vez que todos los lectores en la fuente han terminado de leer.

0

El bloqueo de lector y escritor es lo que necesita. Tutorial tiene una descripción, pero estoy seguro de que alguien estaba agregando esto como estándar a Delphi. Puede valer la pena comprobar que D2009 ya no lo tiene.

1

Nadie realmente puede responder a su pregunta sobre si la serialización afectará mucho el rendimiento de su aplicación: tiene que crear un perfil para usted y los resultados dependerán en gran medida de la cantidad de hilos, núcleos y la carga de trabajo específica.

Sin embargo, tenga en cuenta que el uso de una sincronización más inteligente que las secciones críticas, como el lector-escritor-bloqueo, puede presentar problemas starvation que pueden ser difíciles de depurar y corregir. Realmente necesita mirar con cuidado si el aumento en el rendimiento supera los posibles problemas. Tenga en cuenta también que no puede haber un aumento en el rendimiento, especialmente si el código bloqueado es muy corto y rápido. Hay una nice article by Jeffrey Richter que en realidad contiene esta cita:

Rendimiento Incluso cuando no existe contención para un ReaderWriterLock, su rendimiento es muy lento. Por ejemplo, una llamada a su método AcquireReaderLock tarda aproximadamente cinco veces más en ejecutarse que una llamada al método Enter de Monitor.

Esto es para .NET por supuesto, pero los principios subyacentes sí se aplican también.

+0

Jeffrey Richter está hablando de la implementación de un ReaderWriterLock en la biblioteca .NET. Él mismo presenta una implementación eficiente de dicho bloqueo en el mismo artículo. ¡Pero gracias! Temía que no hubiera una respuesta clara;) – jpfollenius

+1

@Smasher: De hecho. Sin embargo, hubo problemas con la implementación de MREW en el VCL, y la gente realmente no confiaba en él. Jeffrey Richter ha estado haciendo esto por> 15 años, confiaría en su juicio e implementaciones. Solo trata de informarte sobre lo que te estás metiendo. – mghie

Cuestiones relacionadas