使用私有静态方法使单例线程安全吗?

时间:2011-07-29 21:56:18

标签: c# thread-safety singleton static-methods

通过创建一个静态实例,我有一个我想用作单例的类。当然,我也希望它是线程安全的。

假设我没有共享任何私人数据。但是,如果我没有弄错的话,仍然存在这样的问题:当调用静态对象实例的方法时,方法中的变量在线程之间共享,并且会产生不可预测的结果。

但是,在调用真正的静态方法时,会创建一个新的堆栈帧,因此无论如何它都是线程安全的(自身)。如果我没有弄错的话,再一次。

单身人士的这种模式是否是线程安全的?

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;
    }
}

如果你想知道我为什么不首先创建一个静态类 - 我需要一个基于接口的单例,并且具有不同的实现,作为策略模式的一部分。这不适用于静态类。

3 个答案:

答案 0 :(得分:5)

只要您的方法没有从实例方法或全局范围变量获取状态,您的方法就是可重入且线程安全的。它不一定是静态的。如下所示:

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

这是完全线程安全的,可以随意调用。即使在方法内部定义变量也很好,只要它们是方法之间共享的实例变量即可。

一种方法,例如:

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

上述方法也是可重入且线程安全的。

一旦你开始添加变量,无论它们是静态的还是来自不同范围的实例,你都会遇到线程安全问题。

class BadExample
{
    private int counter;

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

在上面的示例中,IncrementCounter()方法不是线程安全的。

答案 1 :(得分:0)

如果我理解你的意思,那么你是对的。

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

即使多个线程调用它,它们也会在堆上创建不同的新变量,并将它们分配给不同堆栈上的Result。

你唯一的危险就是如果你有私人领域。

方法中的变量是临时的,只有该方法可见调用。稍后或并发的方法调用会分别重新创建这些变量。

您唯一关心的是静态或实例字段。那些需要同步。

答案 2 :(得分:0)

您上面的代码是线程安全的,并且由于您指定的原因。我看到的问题是你没有实现单例。

您主要担心线程安全吗?如果是这样,线程安全通常适用于线程之间共享的对象实例。这意味着只要您不跨线程共享普通对象或在类级别创建静态数据,您应该没问题。

我正在添加一个使用单例和接口的例子,有或没有工厂。注意:我没有运行此代码。

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;
    }
}