1
votes

I've got a class I want to use as a singleton by creating a static instance of it. Naturally I also want it to be thread safe.

Assume I am not sharing any private data. But if I am not mistaken, there still remains the problem that when calling a method of a static object instance, variables within methods are shared across threads, and would have unpredictable results.

However, when calling a real static method, a new stack frame is created, so it is thread safe (unto itself) anyway. Again, if I am not mistaken.

Would this pattern for a singleton be thread safe?

class Singleton
{
    public object SomeMethod(object arg) {
        return Singleton.SomeMethodImpl(arg);

    }
    private static object SomeMethodImpl(object arg) {
        // is in unique stack frame?
        object Result;
        ...
        return Result;
    }
}

In case you are wondering why I don't just create a static class in the first place - I need to have a singleton that is based on an interface, and has different implementations, as part of a strategy pattern. This doesn't work for static classes.

3
I would take a look at: dofactory.com/Patterns/PatternSingleton.aspx if you want a good example on how to create a singleton.Steven Behnke
Just skimmed it. Good info - bookmarked. For my purposes now, I don't think I need to worry about locking since I'm not accessing any shared data within the class.Jamie Treworgy

3 Answers

5
votes

As long as your method isn't getting state from an instance method or global scope variable your method is reentrant and thread safe. It doesn't necessarily have to be static. So something like:

int AddTwo(int a, int b)
{
  return a + b;
}

That's perfectly thread safe and can be called as much as you like. Even defining variables inside of the method is fine as long as they are instance variables that are shared between methods.

A method such as:

string ReverseString(string s)
{
    char[] charArray = s.ToCharArray();
    Array.Reverse( charArray );
    return new string( charArray );
}

The method above is also reentrant and thread safe.

As soon as you start adding variables whether they are static or instance that come from a different scope you start having problems with thread safety.

class BadExample
{
    private int counter;

    private void IncrementCounter()
    {
        ++counter;
    }
}

In the above example the IncrementCounter() method is not thread safe.

0
votes

If I understand what you mean, then you're correct.

object Result;   // this is on its unique stack frame and is safe so far
Result = new ... // creating something on the heap that Result points to
                 // still safe because it's the only reference to it

Even if multiple threads call this they will create different new variables on the heap and assign them to Result on different stacks.

The only danger you'd be in is if you had private fields.

Variables within a method are temporary and only visible to that method call. Later or concurrent method calls recreate those variables separately.

Your only concern is static or instance fields. Those would need to be synchronized.

0
votes

The code you have above is thread safe and for the reasons you've specified. The problem I see is you are not implementing a singleton.

Is your main worry about thread safety? If so, thread safety usually applies to object instance being share between threads. That means as long as you don't share a normal object across threads or create static data at the class level you should be ok.

I am adding an example of using singleton with interfaces, with and without a factory. Note: I didn't run this code.

public interface ISomething
{
    void Method();
}

public class Class1 : ISomething
{
    public void Method()
    {
        throw new NotImplementedException();
    }
}

public class Class2 : ISomething
{
    public void Method()
    {
        throw new NotImplementedException();
    }
}

public class Singleton
{
    private static ISomething privateObject;

    public static ISomething Instance()
    {
        lock (privateObject)
        {
            if (privateObject == null)
            {

                privateObject = new Class1();
            }
        }

        return privateObject;
    }
}

public class SingletonUsingFactory
{
    private static ISomething privateObject;

    public static ISomething Instance(int param)
    {
        lock (privateObject)
        {
            if (privateObject == null)
            {
                privateObject = FactoryClass.CreationObject(param);
            }
        }

        return privateObject;
    }
}

public static class FactoryClass
{
    public static ISomething CreationObject(int whatToCreate)
    {
        ISomething createdObject;

        switch (whatToCreate)
        {
            case 0:
                createdObject = new Class1();
                break;
            case 1:
                createdObject = new Class2();
                break;
            default:
                throw new Exception();
        }

        return createdObject;
    }
}