views:

217

answers:

4

So here's an excerpt from one of my classes:

    [ThreadStatic]
    readonly static private AccountManager _instance = new AccountManager();

    private AccountManager()
    {
    }

    static public AccountManager Instance
    {
        get { return _instance; }
    }

As you can see, it's a singleton-per-thread - i.e. the instance is marked with the ThreadStatic attribute. The instance is also instantiated as part of static construction.

So that being the case, how is it possible that I'm getting a NullReferenceException in my ASP.NET MVC application when I try to use the Instance property?

+22  A: 

Quoting MSDN ThreadStaticAttribute:

Do not specify initial values for fields marked with ThreadStaticAttribute, because such initialization occurs only once, when the class constructor executes, and therefore affects only one thread. If you do not specify an initial value, you can rely on the field being initialized to its default value if it is a value type, or to a null reference (Nothing in Visual Basic) if it is a reference type.

Austin Salonen
+1 for providing the reference
Rob Levine
Agreed, I've marked this as the answer. Thanks Austin!
gerrod
A: 

I believe what is happening is that the static field is only being initialized once so when another thread tries to read the field it will be null (since its the default value) because _instance can't be initialized again. Its just a thought but and I could be totally way off but that's what I think is happening.

Zerodestiny
+5  A: 

This is a confusing part of the ThreadStatic attribute. Even though it creates a value per thread, the initalization code is only run on one of the threads. All of the other threads which access this value will get the default for that type instead of the result of the initialization code.

Instead of value initialization, wrap it in a property that does the initialization for you.

[ThreadStatic]
readonly static private AccountManager _instance;

private AccountManager()
{
}

static public AccountManager Instance
{
  get 
  { 
    if ( _instance == null ) _instance = new AccountManager();
    return _instance; 
  }
}

Because the value _instance is unique per thread, no locking is necessary in the property and it can be treated like any other lazily initialized value.

JaredPar
Thanks Jared; my solution looks pretty similar to yours but I didn't know that about ThreadStatic. So fool me!
gerrod
+2  A: 

You've hit a classic [ThreadStatic] "101" here.

The static initializer will only fire once, even though it is marked as [ThreadStatic], so other threads (apart from the first) will see this uninitialised.

Rob Levine