Can readonly field in .NET become null?

I have a strange error in a multi-threaded application:

public class MyClass { private readonly Hashtable HashPrefs; public MyClass(int id) { HashPrefs = new Hashtable(); } public void SomeMethodCalledFromAnotherThread(string hashKey,string hashValue) { if (HashPrefs.Contains(hashKey)) // <-- throws NullReferenceException { } } } 

One thread does:

  SomeQueue.Add(new MyClass(1)); 

And one more thread:

  SomeQueue.Dequeue().SomeMethodCalledFromAnotherThread(SomeClass.SomeMethod(),"const value"); 

But how can the second thread call the method before the constructor completes?

Edit: I added a part with the parameters of the function, since it seems that this may be relevant. As far as I can tell, the hashKey pass cannot be null, as SomeMethod () always returns the corresponding string.

As others pointed out, if the problem was the null haskKey parameter passed to Contains (), the exception would be ArgumentNullException.

+6
multithreading c #
source share
9 answers

Reflection is one way to do this ( SetField ).

The second way to get this curiosity is that you render the link too soon by passing this (or sometihng involving implicit this , for example, a field captured by the anonymous method / lambda) from .ctor :
 public MyClass(int id) { Program.Test(this); // oopsie ;-p HashPrefs = new Hashtable(); } 

Or rather (given the question):

 SomeQueue.Add(this); 

Another important question may be - can it not be assigned in the first place? And the answer to this question is yes, especially if you use serialization. Contrary to popular belief, you can bypass constructors if you have a reason; DataContractSerializer is a good example of what this does ...

The following will create a MyClass with a zero field:

 MyClass obj = (MyClass) System.Runtime.Serialization.FormatterServices.GetUninitializedObject( typeof(MyClass)); 
+11
source share

Yes, readonly fields can be accessed through reflection, and their values ​​are changed. Therefore, the answer to your question is yes, it is possible.

However, there are many other things that can also create problems for you. Multithreaded code is difficult to write, and problems that may arise are difficult to diagnose.


As a side note, are you sure you are not getting an ArgumentNullException ? If somekey is null , the Hashtable.Contains method throws an ArgumentNullException . Perhaps this is a problem?

+7
source share

First of all, another thread cannot access the instance until the constructor is complete, since the instance itself will not be assigned until the constructor completes. In this case, another thread can access the variable that contains the instance until the constructor completes, but at this time it will be null . This would throw a NullReferenceException , but it would come from the instance access method, and not from the method inside the instance.

All this suggests that the only real solution I can see is to use an external lock that synchronizes the code when the instance is retrieved. If you are accessing a Hashset (or any other type in which an instance member is not guaranteed to be thread safe), you still need to lock operations.

Consider the following class as containing a reference to MyClass and the creator of other threads:

 public class MasterClass { private MyClass myClass; private object syncRoot = new object(); // this is what we'll use to synchronize the code public void Thread1Proc() { lock(syncRoot) { myClass = new MyClass(); } } public void Thread2Proc() { lock(syncRoot) { myClas.SomeMethodCalledFromAnotherThread(); } } } 

This is a very (possibly overly) simplistic idea of ​​how synchronization should happen, but the general idea of ​​this approach is to wrap all the code that interacts with the shared object inside the lock block. You really need to learn how to write multi-threaded processes, as well as various approaches aimed at sharing resources between multiple threads.

EDIT

A completely different possibility is that instance variable values ​​are cached depending on the stream. If you are not blocking (which creates a memory barrier), try marking the instance variable as volatile , which ensures that reads and writes will be performed in the correct order.

+3
source share

Your instructions can be reordered so that the reference to the MyClass instance is added to the queue before it is fully built. Here's an article on double lock checking that touches on this topic. From the article:

Vance, devlead in the CLR JIT team explained that the problem is related to the CLR memory model ... In fact, the memory model allows you to save immutable reads / writes, reordering until this change can be seen from the point of view of a single thread.

Take a look at System.Threading.Thread.MemoryBarrier. You may need to do something like this:

  MyClass temp = new MyClass(1); System.Threading.Thread.MemoryBarrier(); SomeQueue.Add(temp); 

MemoryBarrier guarantees that all instructions will be continued before its execution.

+3
source share

Are you sure this is HashPrefs.Contains, which throws an exception, and not something that represents somekey? If you do not, could you post the details of the exception, as returned by the ToString method?

+1
source share

Just do

 HashPrefs = Hashtable.Synchronized(new Hashtable()); 

No need to reinvent the wheel.

+1
source share

Are you absolutely sure that HashPrefs.Contains throwing NPE? I would have thought SomeQueue.Dequeue().SomeMethodCalledFromAnotherThread() would be a more likely candidate ... because the likelihood that the constructor and add operations have not yet ended when you try to delete the item.

In fact, it might be nice to check SomeQueue.Count * before decompressing an element.

Edit: Or, better yet, null check before calling SomeMethodCalledFromAnotherThread, assuming your other thread is checking the queue in the loop ... because if it is not, lock queue before the Add and Dequeue operations.

 MyClass mine = SomeQueue.Dequeue(); if (null != mine) mine.SomeMethodCalledFromAnotherThread(); 

* Extension method added in .NET 3.5.

+1
source share

A few things you can try:

  • Try to initialize the Hashtable as a member variable (outside the constructor) and with a public property of the same name, see if anyone calls its purpose, for example:

     public class MyClass { private readonly Hashtable hashPrefs = new Hashtable(); public MyClass(int id) { } public Hashtable HashPrefs { set { throw new InvalidOperationException("This shouldn't happen"); } } 

    }

  • Secondly, what type of "SomeQueue"? Is this just a regular List <>? Or is it some kind of special self-installed queue that relies on some XML / binary serialization? If so, have you flagged HashPrefs as [Serializable]? Or like [DataMember]?

0
source share

A couple of ideas come to mind:

  • The hash table changes simultaneously, resulting in an inconsistent internal state.
  • The call contains equality checks that attempt to dereference a value that has a zero field. What type of `somekey`?
0
source share

All Articles