使用静态get only属性线程安全吗?

时间:2018-05-11 16:40:27

标签: c# thread-safety

我有这堂课:

class MyFoo
{
    private static readonly string _foo = InitFoo();

    public static string Foo
    {
        get
        {
            return _foo;
        }
    }

    private static string InitFoo()
    {
        Debug.WriteLine("InitFoo"); 
        // do some job
        return "Foo";
    }
}

私有静态_foo成员仅在引用MyFoo.Foo时初始化一次。

InitFoo()返回的数据很大,方法可能很耗时(最多1-2秒),我的问题是,当一个线程引用MyFoo.Foo时,有可能引用它的另一个线程将返回未完成或未初始化的数据b / c InitFoo()尚未完成?

换句话说,上面的线程安全吗?如果不是如何使其线程安全(如果可能的话避免锁定对象?)

感谢。

编辑:关于Lazy<T>的评论是否现在更适合线程安全?:

public sealed class MyFoo
{
    // Explicit static constructor to tell C# compiler not to mark type as beforefieldinit        
    static MyFoo() { }

    private static readonly Lazy<string> _foo = InitFoo();

    public static string Foo
    {
        get
        {
            return _foo.Value;
        }
    }

    private static Lazy<string> InitFoo()
    {
        string s = "Foo";
        return new Lazy<string>(() => s);
    }
}

1 个答案:

答案 0 :(得分:2)

  

当一个线程引用MyFoo.Foo时,是否有可能引用它的另一个线程将返回未完成或未初始化的数据b / c,InitFoo()还没有完成?

没有。类型初始化是线程安全的:

  • 当其他线程被另一个线程
  • 初始化时,没有其他线程可以使用您的类型
  • 初始化线程执行的所有内存写入在执行初始化时对其他线程可见

如果相同的线程初始化MyFoo在完成MyFoo._foo之前完成,那么就会有一个问题。初始化,这将导致问题。如果在循环中有相互依赖的类型进行初始化,那么诊断尤为尴尬。

这是一个例子,有两个类型的初始值设定项,每个都使用另一个的值。它们都具有静态构造函数,以使行为具有确定性。 (初始化类型的规则取决于它们是否具有静态构造函数。)

using System;

public class Program 
{
    public static void Main(string[] args)
    {
        // Determine which type to initialize first based on whether there
        // are any command line arguemnts.
        if (args.Length > 0)
        {
            Class2.DoNothing();
        }
        Console.WriteLine($"Class1.Value1: {Class1.Value1}"); 
        Console.WriteLine($"Class2.Value2: {Class2.Value2}"); 
    }
}

public class Class1
{
    public static readonly string Value1 =
        $"When initializing Class1.Value1, Class2.Value2={Class2.Value2}";

    static Class1() {}
}

public class Class2
{
    public static readonly string Value2 =
        $"When initializing Class2.Value2, Class2.Value2={Class1.Value1}";

    static Class2() {}

    public static void DoNothing() {}
}

在没有任何命令行参数的情况下运行此命令,Class1首先开始初始化,然后初始化Class2

Class1.Value1: When initializing Class1.Value1, Class2.Value2=When initializing Class2.Value2, Class2.Value2=
Class2.Value2: When initializing Class2.Value2, Class2.Value2=

使用任何命令行参数,我们首先初始化Class2,然后初始化Class1

Class1.Value1: When initializing Class1.Value1, Class2.Value2=
Class2.Value2: When initializing Class2.Value2, Class2.Value2=When initializing Class1.Value1, Class2.Value2=