views:

218

answers:

5

I've been using this pattern to initialize static data in my classes. It looks thread safe to me, but I know how subtle threading problems can be. Here's the code:

public class MyClass // bad code, do not use
{
    static string _myResource = "";
    static volatile bool _init = false;
    public MyClass()
    {
        if (_init == true) return;
        lock (_myResource)
        {
            if (_init == true) return;
            Thread.Sleep(3000); // some operation that takes a long time 
            _myResource = "Hello World";
            _init = true;
        }
    }
    public string MyResource { get { return _myResource; } }
}

Are there any holes here? Maybe there is a simpler way to do this.

UPDATE: Consensus seems to be that a static constructor is the way to go. I came up with the following version using a static constructor.

public class MyClass
{
    static MyClass() // a static constructor
    {
        Thread.Sleep(3000); // some operation that takes a long time 
        _myResource = "Hello World";
    }

    static string _myResource = null;

    public MyClass() { LocalString = "Act locally"; } // an instance constructor

    // use but don't modify
    public bool MyResourceReady { get { return _myResource != null; } }
    public string LocalString { get; set; }
}

I hope this is better.

+6  A: 

You can use static constructors to intialize your static variables, which C# guarantees will only be called once within each AppDomain. Not sure if you considered them.

So you can read this: http://msdn.microsoft.com/en-us/library/aa645612(VS.71).aspx (Static Constructors)

And this: http://stackoverflow.com/questions/7095/is-the-c-static-constructor-thread-safe

Moron
A: 

Depending on your use case (i.e. if threads don't need to pass information to each other using this variable), marking the member variable as [ThreadStatic] may be a solution.
See here.

theprise
+3  A: 

Performing a lock() on _myResource and changing it inside lock() statement seems like a bad idea. Consider following workflow:

  1. thread 1 calls MyClass().
  2. execution stops before line _init = true; right after assigning _myResource.
  3. processor switches to thread 2.
  4. thread 2 calls MyClass(). Since _init is still false and refrence _myResource changed, it succesfully enters lock() statement block.
  5. _init is still false, so thread 2 reassigns _myResource.

Workaround: create a static object and lock on this object instead of initialized resource:

private static readonly object _resourceLock = new object();

/*...*/

lock(_resourceLock)
{
    /*...*/
}
max
Good point. I don't really understand the sequence of execution that take place during construction. I see the problem.I think there may also be a problem with assigning a new value to the string. E.G. _myResource = "Some New Value";
RaoulRubin
One more thing to remember: string constants are interned by default (that is, `object.RefererecnceEquals(s1, s2)` will return true for equal string constants s1 and s2), so it's possible to do things like lock("MyString"), but this design is not recommended by MS. That's why using string variable as an argument in `lock()` statement can lead to some unexpected results.
max
+1  A: 

Your class is not safe:

  1. You change the object you're locking on after you've locked on it.
  2. You have a property that gets the resource without locking it.
  3. You lock on a primitive type, which is generally not a good practice.

This should do it for you:

public class MyClass
{
    static readonly object _sync = new object();
    static string _myResource = "";
    static volatile bool _init = false;

    public MyClass()
    {
        if (_init == true) return;
        lock (_sync)
        {
            if (_init == true) return;
            Thread.Sleep(3000); // some operation that takes a long time 
            _myResource = "Hello World";
            _init = true;
        }
    }

    public string MyResource 
    { 
        get 
        { 
            MyClass ret; // Correct
            lock(_sync)
            {
                ret = _myResource;
            }
            return ret;
        } 
    }
}

Update:
Correct, the static resource should not be returned directly... I've corrected my example accordingly.

Lirik
You make a great point. Locking on _myResource was a bad idea. I think there was also a bigger problem - the getter was returning a reference to the static resource. In your example I don't think the get{ lock(_sync) fixes this. Perhaps a better solution would have been to lock, then make a copy and return it. Thanks.
RaoulRubin
String is not a primitive type, but it is still a bad idea to lock on it since if it is interned, your lock object can be exposed outside of the class.
Matt
@Matt, according to msdn a string is a primitive: http://msdn.microsoft.com/en-us/library/ms228360%28VS.80%29.aspx
Lirik
Huh, I guess I was confusing primitive with built-in value type.
Matt
@Matt, In C++ a string is not a primitive type, but in C# and Java it is. A primitive type can be "a built-in type" which "is a data type for which the programming language provides built-in support." http://en.wikipedia.org/wiki/Primitive_data_type
Lirik
@Lirik: It seems that the documentation and the language don't completely agree, though. http://msdn.microsoft.com/en-us/library/system.type.isprimitive.aspx, and typeof(string).IsPrimitive returns false.
Matt
@Matt Yah, there is some discrepancy there... there is another msdn article that defines primitives as: Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, and Single.
Lirik
+1  A: 
static string _myResource = "";
...
public MyClass()
{
    ...
    lock (_myResource)
    {
    }
}

Due to string interning, you should not lock on a string literal.

UPDATE

Yes, @RaoulRubin, so if you lock on a string literal and that string literal is used by multiple classes then you will be sharing that lock. This can potentially cause unexpected behavior.

Tuzo
FYI I had to look up interning. Here's the MSDN: The common language runtime conserves string storage by maintaining a table, called the intern pool, that contains a single reference to each unique literal string declared or created programmatically in your program. Consequently, an instance of a literal string with a particular value only exists once in the system.
RaoulRubin