2011-11-27 9 views
6

Tengo una NullReferenceException increíblemente extraña lanzada cuando leo un valor de un campo público en un objeto que sé que existe. El flujo básico es el siguiente:Subproceso extraño NullReferenceException al leer el valor que existe?

Editar: me di cuenta de que olvidó mencionar algo importante, esto no sucede cada vez que trato de leer el valor Tag, pero sólo somtimes, suficiente como para que pueda reproducirlo cada tiempo con sólo ejecutar el código, pero no instantáneamente cuando se ejecuta el código recibe

  • servidor un mensaje (rosca trabajador)
  • la conexión que envió el mensaje deseando que vuelve como el campo de Tag el objeto de mensaje (rosca trabajador)
  • El mensaje se puso en una cola "ReceivedMessages" (un objeto de cola normal, que está protegido por las cerraduras de acceso serializado) (subproceso de trabajo)
  • El mensaje se lee (Tema principal)
  • trato de leer el campo del mensaje Tag para conseguir la conexión, esto a veces vuelve nula y se produce una excepción, pero cuando la excepción es lanzada y yo inspeccionar el objeto Message que puede ver el objeto Connection (que es el objeto que está en el campo Tag) hay un claro s días (Tema principal)

Si nos fijamos en esta imagen, usted lo verá claro como el agua:

Weird thread behavior

Se puede ver donde he marcado con la caja verde, me intente leer la propiedad message.Tag de tres maneras diferentes, todas devuelven nulo como puede ver en la parte marcada con un cuadro azul.

Sin embargo, si observa las dos áreas marcadas como rojas, puede ver claro como el día en que el objeto realmente existe. Y, solo para despejar cualquier confusión, la parte donde se coloca el mensaje en la cola de mensajes recibidos se ve así:

Como puede ver, incluso intenté hacer un Thread.VolatileWrite para asegurarme de que se escribe el valor

message.Tag = buffer.Tag; 

Thread.VolatileWrite(ref message.Tag, buffer.Tag); 

if (message.Tag == null) 
{ 
    isNullLog.Add(message.Id); 
} 

// Queue into received messages 
lock (peer.ReceivedMessages) 
{ 
    peer.ReceivedMessages.Enqueue(message); 
} 

el fragmento anterior está sucediendo en el subproceso de trabajo, y como se puede ver copio el buffer.Tag a message.Tag, que incluso configurar una pequeña comprobación en tiempo de ejecución para la depuración que comprueba el message.Tag por un valor nulo y agregue su identificación a una lista llamada "isNullLog" si es así. Cuando se lanza la NullReferenceException en el hilo principal, esta lista está vacía.

También ver que yo cierro la cola peer.ReceivedMessages y empuje el mensaje a la cola después de he puesto el campo message.Tag.

Además, para ser aún más claro aquí es la función que se utiliza para leer un mensaje de la cola peer.ReceivedMessages:

public bool TryGetMessage(out TIncomingMessage message) 
{ 
    lock (ReceivedMessages) 
    { 
     if (ReceivedMessages.Count > 0) 
     { 
      message = ReceivedMessages.Dequeue(); 
      return true; 
     } 
    } 

    ReceivedMessageEvent.Reset(); 

    message = null; 
    return false; 
} 

Se puede ver que cierro la cola, incluso antes de comprobar el recuento, y si no está vacío, configuro la propiedad out y devuelvo true; de ​​lo contrario, devuelvo falso.

Honestamente, estoy completamente perplejo, escribí varias aplicaciones de subprocesos múltiples antes y nunca me he encontrado con esto.

Un poco de una actualización, también he intentado marcar el campo Tag como volatile, haciendo que parezca que esta public volatile object Tag; pero esto no parece estar ayudando.

+2

No soy un erudito, pero creo que estás mezclando bloqueos con lecturas volátiles y escribe ... Creo que este es el problema. Si usa volátiles, es mejor que lo use en todo momento ... Si usa bloqueos, solo se bloquea ... –

+0

Hm, no estoy seguro si acepto - Tengo bloqueos donde se juntan todos los hilos, la parte volátil no parece afectar en absoluto (como en nada sucede si lo elimino o lo agrego). – thr

+1

¿Hay algún otro subproceso que opere en el campo 'message.Tag' (por ejemplo, establecerlo como nulo)? – Hans

Respuesta

2

De hecho, solucioné esto ahora, como siempre cuando trato con hilos, debe tener mucho cuidado al leer/escribir valores. Olvidé borrar la variable local message en el bucle de recepción y terminé "reutilizando" el mismo mensaje en la siguiente iteración de bucle, ya que tiene una marca if(message == null) { /* create new message */ } antes de cada iteración y cuando no estaba despejando esto, el hilo de lectura terminó pisoteando todo el "viejo" mensaje que se almacenó aquí cuando intentaba escribir un nuevo mensaje para él.

+1

Puede tener demasiadas comprobaciones :) No se moleste en verificar o borrar el mensaje * variables de instancia de objeto - solo adquiera el hábito de crear una nueva instancia al inicio y como el siguiente paso después de poner en cola una. –

Cuestiones relacionadas