通过使用私有静态方法使单例线程安全

本文关键字:单例 线程 静态方法 安全 | 更新日期: 2023-09-27 18:04:04

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

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

然而,当调用一个真正的静态方法时,会创建一个新的堆栈帧,因此它无论如何都是线程安全的。再说一遍,如果我没弄错的话。

单例模式是线程安全的吗?

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

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

通过使用私有静态方法使单例线程安全

只要你的方法不是从实例方法或全局作用域变量获取状态,你的方法就是可重入的和线程安全的。它不一定是静态的。比如:

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()方法不是线程安全的。

如果我明白你的意思,那么你是对的。

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。

唯一的危险是如果你有私有字段。

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

您唯一关心的是静态或实例字段。

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

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

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

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