I have a situation where very rarely a Queue of Objects is dequeuing a null. The only call to Enqueue is within the class itself:
m_DeltaQueue.Enqueue(this);
Very rarely, a null is dequeued from this queue in the following code (a static method):
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); }
This is the error report that was generated:
[Jan 23 01:53:13]: m_DeltaQueue.Dequeue of a null occurred: m_DeltaQueue is not null. m_DeltaQueue.count:345
I'm very confused as to how a null value could be present in this queue.
As I'm writing this, I'm wondering if this could be a failure of thread synchronization; this is a multi threaded application and It's possible the enqueue or dequeue could be happening simultaneously in another thread.
This is currently under .Net 4.0, but it previously occurred in 3.5/2.0
Update:
This is my (hopefully correct) solution to the problem which was made clear though the great answers below as being a synchronization problem.
private static object _lock = new object(); private static Queue<Mobile> m_DeltaQueue = new Queue<Mobile>();
Enqueue:
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();
I'm still trying to get a handle on proper syncronization, so any comments on the correctness of this would be very appreciated. I initially chose to use the queue itself as a syncronization object because it's private, and introduces less clutter into what is already a very large class. Based on John's suggestion I changed this to lock on a new private static object, _lock.
The #define creates a macro, which is the association of an identifier or parameterized identifier with a token string. After the macro is defined, the compiler can substitute the token string for each occurrence of the identifier in the source file.
Logical OR operator: || The logical OR operator ( || ) returns the boolean value true if either or both operands is true and returns false otherwise. The operands are implicitly converted to type bool before evaluation, and the result is of type bool .
Keywords are predefined, reserved words used in programming that have special meanings to the compiler. Keywords are part of the syntax and they cannot be used as an identifier.
this
can never be null, unless the method was called using a call
instruction in hand-written IL.
However, if you use the same Queue
instance on multiple threads simultaneously, the queue will become corrupted and lose data.
For example, if two items are added simultaneously to a near-capacity queue, the first item might be added to the array after the second thread resizes it, which will end up copying a null
to the resized array and adding the first item to the old array.
You should protect your queues with locks or use .Net 4's ConcurrentQueue<T>
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With