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 objetoMessage
que puede ver el objetoConnection
(que es el objeto que está en el campoTag
) hay un claro s días (Tema principal)
Si nos fijamos en esta imagen, usted lo verá claro como el agua:
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.
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 ... –
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
¿Hay algún otro subproceso que opere en el campo 'message.Tag' (por ejemplo, establecerlo como nulo)? – Hans