C# 使用静态get-only属性线程安全?

C# 使用静态get-only属性线程安全?,c#,thread-safety,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")

我有这门课:

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";
    }
}
私有静态
\u foo
成员在引用
MyFoo.foo
时仅初始化一次

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

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

谢谢


编辑:按照关于
Lazy
的注释进行操作,它现在对线程安全更好吗

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);
    }
}
公共密封类MyFoo
{
//显式静态构造函数告诉C#编译器不要将类型标记为beforefieldinit
静态MyFoo(){}
私有静态只读惰性_foo=InitFoo();
公共静态字符串Foo
{
得到
{
返回_foo.Value;
}
}
私有静态惰性InitFoo()
{
字符串s=“Foo”;
返回新的延迟(()=>s);
}
}
当一个线程引用MyFoo.Foo时,另一个引用它的线程是否有可能返回未完成或未初始化的数据b/c InitFoo()尚未完成

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

  • 当您的类型被另一个线程初始化时,没有其他线程可以使用它
  • 初始化线程执行的所有内存写入操作在执行初始化后对其他线程可见
有一个问题是,如果初始化
MyFoo
的同一线程在完成初始化之前读取了
MyFoo.\u foo
,这将导致问题。如果在一个周期中存在相互依赖的初始化类型,则诊断这种类型可能会特别困难

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

在没有任何命令行参数的情况下运行此命令,
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=

使用
Lazy
,应该保证线程安全。@maccettura,我如何将上述更改为使用
Lazy
?我应该在静态成员上使用它还是什么?Jon Skeet有一个很好的介绍。你应该可以直接从there@maccettura,但我不想要单例类实例。我将该类用作静态类。你能举一个例子说明如何使用Lazy来实现上述功能吗?无论出于何种目的,你的
\u foo
/
foo
属性都是单例的。它们是类的只读静态成员,这意味着它将在所有实例之间共享,并且永远无法创建新实例。你的问题是它目前不是线程安全的,所以如果你看看我发布的链接,你可以让它成为线程安全的。哇,你怎么能避免这种行为呢?@zig:注意你在静态初始化器中的操作,基本上:)那么,我的代码是否是线程安全的?你也可以看看我的编辑吗?@zig:你不需要在这里偷懒,不。你不是想拖延任何事情。已经安全了,谢谢。现在这个问题对我来说更清楚了,特别是在我读了这篇文章之后:
Class1.Value1: When initializing Class1.Value1, Class2.Value2=
Class2.Value2: When initializing Class2.Value2, Class2.Value2=When initializing Class1.Value1, Class2.Value2=