Signalr: InvalidOperationException cuando se establece la propiedad de elemento de HubProxy durante la ejecución del método de invocación de HubProxy en el cliente de SignalR .Net

Creado en 4 jun. 2015  ·  10Comentarios  ·  Fuente: SignalR/SignalR

Impacto funcional

Encontré que InvalidOperationException se lanza con la condición de bloqueo de HubProxy _state.
Utilizo la última versión de Microsoft.AspNet.SignalR.Client (2.2.0).

Pasos de reproducción

  1. Crear HubProxy
  2. Ejecutar por debajo del método 2 en parrarel

    1. establecer la propiedad del elemento HubProxy

    2. ejecutar el método de invocación de HubProxy

      Resultado Esperado


El método de invocación se ejecuta correctamente.

Resultado actual

Entonces, el método Invoke puede emitir una InvalidOperationException con "La colección fue modificada; la operación de enumeración puede no ejecutarse".

Más detalles técnicos

La propiedad de elemento de HubProxy (indexador) obtiene o establece el campo "_state" con la declaración de bloqueo.
https://github.com/SignalR/SignalR/blob/master/src/Microsoft.AspNet.SignalR.Client/Hubs/HubProxy.cs#L27 -L45

El "_state" es IDictionaryy este campo pasa a la variable local "hubData" en el método Invoke.
https://github.com/SignalR/SignalR/blob/master/src/Microsoft.AspNet.SignalR.Client/Hubs/HubProxy.cs#L159 -L172

Por lo tanto, se lanza InvalidOperationException si la propiedad Item se establece durante la ejecución del método Invoke.
Esto se debe a que el diccionario de la variable "hubData" está enumerado en la biblioteca Json.Net.
https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs#L926

Solo una idea, este código solucionará este problema.

lock(_state)
{
    if (_state.Count != 0)
    {
        hubData.State = _state;
    }

    var value = _connection.JsonSerializeObject(hubData);
}

Comentario más útil

El OP menciona que las llamadas paralelas al indexador e Invoke exponen este error. Sin embargo, parece que dos llamadas paralelas a Invoke también expondrán el problema, ya que la devolución de llamada establece el estado a través de ese mismo indexador. https://github.com/SignalR/SignalR/blob/master/src/Microsoft.AspNet.SignalR.Client/Hubs/HubProxy.cs#L128
La devolución de llamada puede establecer el estado, mientras que la llamada de otro hilo a Invoke está serializando (y por lo tanto enumerando) el estado. Veo este problema en algunas pruebas automatizadas que crean un proxy y llaman a varios métodos de concentrador en paralelo. Es un escenario del mundo real para llamar a métodos de concentrador en paralelo, por lo que este es un error bastante grave en mi opinión.

Todos 10 comentarios

¿Por qué desea establecer el estado y llamar a Invoke en paralelo? El estado se envía al servidor cuando llama a Invoke, por lo que estas llamadas deben serializarse; de ​​lo contrario, hay una condición de carrera y es posible que los datos de estado que establezca no se envíen al servidor. Además, el indexador es un método sincrónico, por lo que configurarlo "en paralelo con Invoke" significa que usted crea sus propios subprocesos, en cuyo caso debe garantizar el orden correcto de las llamadas o primero invoca el método Invoke sin esperar y luego establece el estado en cuyo caso probablemente debería reordenar las llamadas.
¿Puede elaborar más sobre su escenario?

Encontré este problema mientras escribía una herramienta de prueba de esfuerzo para SignalR WebServer. Varios subprocesos de trabajo se ejecutan en paralelo y estos subprocesos comparten la misma instancia de HubProxy.
Entiendo que mi caso es un caso especial. Pero HubProxy debería ser una clase segura para subprocesos. En realidad, el archivo _state está bloqueado durante la propiedad del artículo (indexador). El método de invocación debe bloquear el campo _state de la misma manera.

Creo que este es el mismo problema que planteé aquí: https://github.com/SignalR/SignalR/issues/3631

Estoy de acuerdo con tanaka, hay un bloqueo inconsistente alrededor de _state. Ya sea que sea seguro para subprocesos o no, la existencia de bloqueo me sugiere que debería ser seguro para subprocesos.

Casi un año sin comentarios :-(

El OP menciona que las llamadas paralelas al indexador e Invoke exponen este error. Sin embargo, parece que dos llamadas paralelas a Invoke también expondrán el problema, ya que la devolución de llamada establece el estado a través de ese mismo indexador. https://github.com/SignalR/SignalR/blob/master/src/Microsoft.AspNet.SignalR.Client/Hubs/HubProxy.cs#L128
La devolución de llamada puede establecer el estado, mientras que la llamada de otro hilo a Invoke está serializando (y por lo tanto enumerando) el estado. Veo este problema en algunas pruebas automatizadas que crean un proxy y llaman a varios métodos de concentrador en paralelo. Es un escenario del mundo real para llamar a métodos de concentrador en paralelo, por lo que este es un error bastante grave en mi opinión.

¿Por qué necesita llamar a métodos de concentrador en paralelo? ¿Por qué no solucionar el problema pero serializar las llamadas al proxy del concentrador?

De hecho, puedo solucionarlo. Sin embargo, como dijeron goldenc y daniel-smith, hay un bloqueo inconsistente.
En particular, veo este problema en mis propias pruebas de carga.
Si deberíamos llamar a hubproxy en serie, sería mejor eliminar todas las declaraciones de bloqueo alrededor de _state debido al rendimiento.
Si podemos llamar a hubproxy en parrarel, sería mejor agregar declaraciones de bloqueo alrededor de _state .

De acuerdo, solo me aseguro de que esto no te bloquee

La razón por la que llamamos a las cosas en paralelo es simplemente porque esperamos múltiples resultados de solicitud / respuesta, potencialmente de larga duración, en paralelo. Incluso si serializamos las llamadas a Invoke, habría pensado que no teníamos control sobre el hilo de devolución de llamada, por lo que no estoy seguro de que esto resolvería el problema. Es decir, no pudimos serializar el trabajo de devolución de llamada, que aún puede actualizar el estado mientras se realiza otra llamada serializada a Invoke.

Este problema se ha cerrado como parte de la limpieza de problemas como se describe en https://blogs.msdn.microsoft.com/webdev/2018/09/17/the-future-of-asp-net-signalr/. Si aún tiene este problema, no dude en volver a abrir y comentar para hacérnoslo saber. Todavía estamos interesados ​​en saber de usted, la acumulación se hizo un poco grande y tuvimos que hacer una limpieza masiva para volver a estar al tanto de las cosas. ¡Gracias por sus continuos comentarios!

¿Fue útil esta página
0 / 5 - 0 calificaciones