从静态方法线程安全返回引用?

时间:2012-04-27 17:48:10

标签: c# multithreading

我有一个班级:

class PrintStringDataBuilder
{
    PrintStringDataBuilder() { }
    public static GetInstance()
    {
        return new PrintStringDataBuilder();
    }

    //other class methods and fields, properties
}

从客户代码访问:

PrintStringDataBuilder instance = PrintStringDataBuilder.GetInstance();

以上是线程安全的吗?

编辑:试图避免写作 PrintStringDataBuilder builder = new PrintStringDataBuilder();在asp.net mvc web app中多次。 PrintStringDataBuilder类中没有其他静态方法,静态字段或静态属性。

4 个答案:

答案 0 :(得分:11)

是?在不知道该类的构造函数的内部结构的情况下,您可以说调用GetInstance()是线程安全的。但是,该实例上的任何方法都不能保证是线程安全的,特别是因为您没有提供任何这些方法。

这简称为工厂模式。

编辑:如果你想要返回一个单身人士,你可以这样做:

.NET 4 +

private static Lazy<PrintStringDataBuilder> _instance = new Lazy<PrintStringDataBuilder>(() =>
  {
      return new PrintStringDataBuilder();
  });

public static PrintStringDataBuilder GetInstance()
{
    return _instance.Value;
}

.NET 3.5及以下

private static PrintStringDataBuilder _instance = null;
private static object _lockObject = new object();

public static PrintStringDataBuilder GetInstance()
{
    if(_instance == null)
    {
         lock(_lockObject)
         {
              if(_instance == null)
                 _instance = new PrintStringDataBuilder();
         }
    }

    return _instance;
}

答案 1 :(得分:5)

通过'threadsafe',您是否担心调用静态方法的多个线程将获得SAME PrintStringDataBuilder?答案是否定的,并且调用是线程安全的。

话虽如此,没有人能从你给出的小片段中判断出其余的是否是它的构造函数。类实例不是线程安全的原因有很多。如果他们在没有锁定的情况下引用静态属性就是一个例子。

答案 2 :(得分:3)

输入方法始终是线程安全的。访问共享数据可能不是。所以这段代码是线程安全的,因为没有共享数据。

如果您的目的是为所有线程设置一个PrintStringDataBuilder实例,那么为此目的,您的代码将无效。你需要适当的单身人士。在.NET 4中,代码可以非常紧凑:

private static Lazy<PrintStringDataBuilder> instance = new Lazy<PrintStringDataBuilder>();

public static PrintStringDataBuilder Instance
{
    get { return instance.Value; }
}

这将保证在每个线程中PrintStringDataBuilder.Instance将指向将以惰性方式创建的PrintStringDataBuilder对象的相同且仅一个实例,即仅在首次使用时才会创建

答案 3 :(得分:1)

@Tejs,

实际上,在.NET中,您不需要使用双重检查锁定机制 - 有更好的方法来解决它。但是如果您选择这样做,那么双重检查锁的实现是不正确的,并且不是真正的线程安全的。编译器可以优化_instance = new PrintStringDataBuilder();的初始化 - 有3种可能的修改使您的示例真正是线程安全的:

  1. 初始化静态成员内联 - 绝对是最简单的!
  2.     private static PrintStringDataBuilder _instance = new PrintStringDataBuilder;
        public static PrintStringDataBuilder GetInstance()
        {
            return _instance;
        }
    

    2。使用'volatile'关键字来确保JIT不优化PrintStringDataBuilder的初始化。

    
    private static volatile PrintStringDataBuilder _instance = null;
    private static object _lockObject = new object();
    
    public static PrintStringDataBuilder GetInstance()
    {
        if(_instance == null)
        {
             lock(_lockObject)
             {
                  if(_instance == null)
                  {
                     _instance = new PrintStringDataBuilder();
                  }
             }
        }
    
        return _instance;
    }

    3。使用Interlocked.Exchange和双重检查锁:

    
    private static PrintStringDataBuilder _instance = null;
    private static object _lockObject = new object();
    
    public static PrintStringDataBuilder GetInstance()
    {
        if(_instance == null)
        {
             lock(_lockObject)
             {
                  if(_instance == null)
                  {
                     var temp = new PrintStringDataBuilder();
                     Interlocked.Exchange(ref _instance, temp);
                  }
             }
        }
    
        return _instance;
    }

    希望这有帮助。