c# - microsoft - visual studio installer
Dentro de un método de instancia de C#, ¿puede "esto" ser nulo? (6)
Tengo una situación en la que muy raramente una Cola de objetos se está eliminando un nulo. La única llamada a Enqueue está dentro de la propia clase:
m_DeltaQueue.Enqueue(this);
Muy raramente, un nulo se saca de esta cola en el siguiente código (un método estático):
while (m_DeltaQueue.Count > 0 && index++ < count)
if ((m = m_DeltaQueue.Dequeue()) != null)
m.ProcessDelta();
else if (nullcount++ < 10)
{
Core.InvokeBroadcastEvent(AccessLevel.GameMaster, "A Rougue null exception was caught, m_DeltaQueue.Dequeue of a null occurred. Please inform an developer.");
Console.WriteLine("m_DeltaQueue.Dequeue of a null occurred: m_DeltaQueue is not null. m_DeltaQueue.count:{0}", m_DeltaQueue.Count);
}
Este es el informe de error que se generó:
[23 de enero 01:53:13]: m_DeltaQueue.Dequeue ocurrió un nulo: m_DeltaQueue no es nulo. m_DeltaQueue.count: 345
Estoy muy confundido en cuanto a cómo un valor nulo podría estar presente en esta cola.
Mientras escribo esto, me pregunto si esto podría ser una falla en la sincronización de subprocesos; esta es una aplicación de varios subprocesos y es posible que la puesta en cola o la salida de la cola puedan estar ocurriendo simultáneamente en otro subproceso
Esto está actualmente bajo .Net 4.0, pero ocurrió anteriormente en 3.5 / 2.0
Actualizar:
Esta es mi solución (esperemos que correcta) para el problema, que se aclaró a través de las grandes respuestas a continuación como un problema de sincronización.
private static object _lock = new object();
private static Queue<Mobile> m_DeltaQueue = new Queue<Mobile>();
En cola:
lock (_lock)
m_DeltaQueue.Enqueue(this);
Dequeue:
int count = m_DeltaQueue.Count;
int index = 0;
if (m_DeltaQueue.Count > 0 && index < count)
lock (_lock)
while (m_DeltaQueue.Count > 0 && index++ < count)
m_DeltaQueue.Dequeue().ProcessDelta();
Todavía estoy intentando controlar la sincronización correcta, por lo que cualquier comentario sobre la corrección de esto sería muy apreciado. Inicialmente opté por usar la cola como un objeto de sincronización porque es privado, e introduce menos desorden en lo que ya es una clase muy grande. Según la sugerencia de John, cambié esto para bloquear un nuevo objeto estático privado, _lock.
(Un poco fuera de tema y una posibilidad muy poco probable; he creado esta wiki de la comunidad. La pregunta real ya se ha resuelto; esto se relaciona principalmente con el título de la pregunta).
En teoría, si su código m_DeltaQueue.Enqueue(this)
dio lugar a la invocación de un operador de conversión implícito en el argumento, eso podría dar lugar a que se pase una referencia nula al método.
class Foo
{
public static implicit operator string(Foo foo)
{
return null;
}
void InstanceMethod()
{
string @this = this;
if (@this == null)
Console.WriteLine("Appears like ''this'' is null.");
}
static void Main()
{
new Foo().InstanceMethod();
}
}
De hecho, si la clase de cola que está utilizando no es segura para subprocesos, podría estar en cola desde dos subprocesos al mismo tiempo. La forma más fácil de evitar esto es bloquear su cola cuando se está retirando de ella.
//declare this object in a globally accessible location
object locker = new object();
lock(locker)
{
m = mDeltaQueue.Dequeue();
}
Es posible crear un delegado que llame a un método de instancia en una instancia null
utilizando la sobrecarga Delegate.CreateDelegate(Type, object, MethodInfo)
.
El MSDN dice (énfasis mío)
Si firstArgument es una referencia nula y el método es un método de instancia, el resultado depende de las firmas del tipo de tipo delegado y del método :
- Si la firma de tipo incluye explícitamente el primer parámetro oculto del método , se dice que el delegado representa un método de instancia abierta. Cuando se invoca al delegado, el primer argumento de la lista de argumentos se pasa al parámetro de instancia oculta del método .
- Si las firmas de método y tipo coinciden (es decir, todos los tipos de parámetros son compatibles), se dice que el delegado está cerrado por una referencia nula . Invocar al delegado es como llamar a un método de instancia en una instancia nula , lo que no es particularmente útil.
Las colas no son intrínsecamente seguras para subprocesos. Este es tu problema. Use un mutex / lock / lo que sea o busque una cola segura de subprocesos.
this
nunca puede ser nulo (el CLR generará una excepción si intenta llamar a un método en null
). Es casi seguro que tenga un error de sincronización, donde dos subprocesos intentan agregarse a la cola simultáneamente. Quizás ambas hebras están incrementando un índice en la matriz y luego colocando su valor en la misma ubicación. Esto significa que el primer hilo está sobrescribiendo su valor.
Sincronice su acceso (por ejemplo, con lock
) o use una ConcurrentQueue
(en .Net 4).
this
nunca puede ser nulo, a menos que se llame al método utilizando una instrucción de call
en IL escrita a mano.
Sin embargo, si utiliza la misma instancia de Queue
en varios subprocesos simultáneamente, la cola se corromperá y perderá datos.
Por ejemplo, si dos elementos se agregan simultáneamente a una cola de casi capacidad, el primer elemento puede agregarse a la matriz después de que el segundo subproceso la redimensiona, lo que terminará copiando un null
a la matriz redimensionada y agregando el primer elemento a la matriz. antiguo conjunto.
Debe proteger sus colas con bloqueos o usar la ConcurrentQueue<T>
.Net 4.