2010-03-30 8 views
23

Tenemos un servicio WCF que realiza una buena cantidad de llamadas NHibernate transaccionales. De vez en cuando, estábamos viendo tiempos de espera de SQL, a pesar de que las llamadas estaban actualizando diferentes filas y las tablas se establecieron en el nivel de fila de bloqueo.¿Son posibles problemas de concurrencia cuando se usa el atributo Comportamiento del servicio WCF establecido en ConcurrencyMode.Multiple e InstanceContextMode.PerCall?

Después de indagar en los registros, parece que los diferentes subprocesos ingresaban en el mismo punto en el código (nuestra transacción usando el bloque), y una actualización estaba pendiente de confirmación. No tenía sentido, sin embargo, debido a que creemos que el atributo siguiente clase de servicio estaba forzando un hilo de ejecución única por cada llamada de servicio:

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerCall)] 

Recientemente hemos cambiado el modo de concurrencia a ConcurrencyMode.Single y aún no se han topado cualquier problema, pero el error fue muy difícil de reproducir (si alguien tiene alguna idea sobre eliminar un error como este, ¡hágamelo saber!).

De todos modos, eso me lleva a mi pregunta: ¿no debería un InstanceContextMode of PerCall imponer la seguridad de subprocesos dentro del servicio, incluso si el modo ConcurrencyMode está configurado en varios? ¿Cómo sería posible que dos llamadas sean atendidas por la misma instancia de servicio?

Gracias!

Respuesta

24

La única forma de tener dos clientes WCF diferentes, es decir, proxies, hacer referencia a la misma instancia de su servicio WCF es usar InstanceContextMode=InstanceContextMode.Single. Esta es una mala elección si la escala es un problema, por lo que desea utilizar PerCall si puede.

Cuando se utiliza PerCall, cada LLAMADA al servicio WCF tiene su propia instancia de servicio WCF. No se comparte la instancia de servicio, pero eso no significa que no compartan el mismo almacenamiento de fondo (por ejemplo, base de datos, memoria, archivo, etc.). Solo recuerda, PerCall permite que cada llame al para acceder a tu servicio WCF simultáneamente.

La configuración ConcurrencyMode controla el modelo de subprocesamiento del servicio en sí. Una configuración de Single restringe todas las instancias del servicio WCF para ejecutarse en el mismo subproceso. Por lo tanto, si tiene varios clientes conectados al mismo tiempo, solo se ejecutarán uno a la vez en el lado del servicio WCF. En este caso, aprovecha el WCF para proporcionar sincronización. Funcionará bien, como ha visto, pero piense que esto solo tiene control de nivel macro sobre la sincronización: cada llamada al servicio WCF se ejecutará en su totalidad antes de que se pueda ejecutar la próxima llamada.

Sin embargo, la configuración ConcurrencyMode a , permitirá que todas las instancias de servicio WCF se ejecuten simultáneamente. En este caso, , usted es responsable de proporcionar la sincronización necesaria. Piense en esto como que tiene un control de micro nivel sobre la sincronización, ya que puede sincronizar solo aquellas partes de cada llamada que necesitan sincronizarse.

espero que he explicado esta bastante bien, pero aquí es un fragmento de la documentación de MSDN para ConcurrencyMode por si acaso:

Ajuste ConcurrencyMode a Single instruye al sistema para restringir instancias del servicio a uno el hilo de la ejecución a la vez, que libera de ocuparse de threading issues. Un valor de Múltiple significa que los objetos de servicio se pueden ejecutar por múltiples hilos en cualquier momento. En este caso, debe garantizar la seguridad del hilo .

EDITAR

Se preguntó

¿Hay alguna aumento de rendimiento y, a continuación, utilizando PerCall vs. individual al utilizar ConcurrencyMode.Single? ¿O es el inverso verdadero?

Esto probablemente dependerá del servicio.

Con InstanceContextMode.PerCall, se crea una nueva instancia de servicio para todas y cada una de las llamadas a través del proxy, por lo que tiene que encargarse de la creación de instancias. Suponiendo que su constructor de servicio no hace mucho, esto no será un problema.

Con InstanceContextMode.Single, solo existe una instancia de servicio durante la vida útil de la aplicación, por lo que prácticamente no hay gastos generales asociados con la creación de la instancia. Sin embargo, este modo permite que solo una instancia de servicio procese cada llamada que se realice. Por lo tanto, si tiene varias llamadas simultáneas, cada llamada tendrá que esperar a que finalicen las otras para poder ejecutarla.

Por lo que vale, así es como he hecho esto. Use el contexto de instancia PerCall con concurrencia Multiple. Dentro de la clase de servicio WCF, cree miembros estáticos para administrar el almacén de datos de fondo y luego sincronice el acceso a estos miembros estáticos según sea necesario usando la instrucción lock, volatile campos, etc. Esto permite que su servicio se amplíe bien mientras se mantiene hilo de seguridad.

+0

Definitivamente has clavado mi otra desconexión. Así que usar 'ConcurrencyMode.Multiple', independientemente de InstanceContextMode, significa que estás usando sentencias' lock' alrededor de secciones críticas y realizando otras comprobaciones de seguridad/sincronización de hilos. ¿Hay algún aumento de rendimiento, entonces, usando 'PerCall' contra' Single' cuando se usa 'ConcurrencyMode.Single'? ¿O es el inverso verdadero? –

+0

http://msdn.microsoft.com/en-us/library/ms731193.aspx –

+0

Esta es toda la información excelente. Gracias por ser tan completo; Saqué un montón de eso. –

7

Creo que la respuesta es el hecho de que hay varios subprocesos (en el lado del cliente) que utilizan la misma instancia de proxy, lo que potencialmente permite varias llamadas en la misma instancia. Este post tiene una explicación más detallada.

+0

Ese es un gran artículo y creo que realmente ilumina el problema (almacenamos en la memoria caché el proxy en el nivel de dominio de la aplicación en el lado del cliente, que utilizan varias solicitudes web). También lo son las opciones 1) Para que las operaciones de servicio sean seguras para hilos y abiertas por llamada a múltiples hilos o 2) simplemente déjalo como único y toma el golpe de rendimiento. ¡Solo quiero asegurarme de no perderme nada! –

+0

O supongo que potencialmente una opción 3 sería crear un nuevo proxy para cada solicitud ...? –

+0

@Brandon Linton, utilizando un nuevo proxy para cada solicitud no resolverá el problema si utiliza el contexto de instancia 'PerCall' y el modo' Multiple' de concurrencia. En este caso, ** usted ** debe proporcionar la seguridad del hilo usted mismo ya sea que cree el proxy cada vez o reutilice el mismo una y otra vez. –

2

InstanceContextMode.PerCall y ConcurrencyMode.Single debería estar bien si no utiliza devoluciones de llamada bidireccionales en el servidor. En ese caso, deberá usar ConcurrencyMode.Reentrant o la devolución de llamada no podrá acceder a la instancia de servicio bloqueada y se producirá un interbloqueo.

Como es una creación de instancia de servicio por llamada, es imposible que otros subprocesos o llamadas accedan a ella. Como se menciona en el artículo mencionado en otras respuestas, article, esta combinación aún puede ser un problema si la sesión se crea en un nivel de enlace Y está utilizando el mismo objeto proxy de servicio.

Así que si no utiliza el mismo objeto proxy o no tiene un enlace de sesión y no utiliza devoluciones de llamada bidireccionales al cliente (probablemente deberían ser OneWay de todos modos) InstanceContextMode.PerCall y ConcurrencyMode.Single deberían ser buenos.

1

Creo que todo depende del requisito. Si vamos a llamar al mismo servicio tantas veces, mejor podemos usar InstanceContextMode es Single y el modo concurrency es múltiple.

Cuestiones relacionadas