【问题标题】:C# Singleton Thread SafetyC# 单例线程安全
【发布时间】:2014-10-15 17:17:18
【问题描述】:

我阅读了 Jon Skeet 关于如何实现 C# 单例的权威 post 并遵循以下模式。请注意,我没有空的 ctor。我的 Ctor 可能会做一些工作,例如创建和填充字符串数组(或者它可能会创建一些对象并将它们分配给私有变量等):

public class MyClass
    {                
        /// <summary>
        /// Get singleton instance of this class.
        /// </summary>
        public static readonly MyClass Instance = new MyClass();


        /// <summary>
        /// a collection of strings.
        /// </summary>
        private string[] strings;        

        private MyClass()
        {            
            this.strings = new string[]
            {
                "a",
                "b",
                "c",
                "d",
                "e" 
            };
        }

        public void MyMethod()
        {
           // tries to use this.strings.
           // can a null ref exception happen here when running multithreaded code?
        }
}

是否高于线程安全?我之所以问,是因为我在 asp.net appserver 上运行了类似的代码,并在日志中获取了 null ref 异常(不确定 null ref 是否与上述相关 - 我认为不是 - 并且日志中的调用堆栈没有帮助)。

【问题讨论】:

  • Yes, the initialization is thread-safe。它保证只发生一次,并且只会实例化您的类的一个实例。
  • 是的,它是线程安全的。一个额外的信息 - 如果你想要单例“每个线程”,你可以用 ThreadStatic 属性标记静态字段。

标签: c# thread-safety


【解决方案1】:

老实说,我看不出它不应该是线程安全的原因。特别是考虑到 Jon 的第四个线程安全版本本质上是相同的。

我看到的唯一问题是您没有静态构造函数。 (这可能会导致问题,请参阅this)如果您添加静态构造函数(即使它是空的),您将拥有 Jon Skeet 所说的线程安全。

public class MyClass
{
    public static readonly MyClass Instance = new MyClass();

    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    static MyClass() { }
}

【讨论】:

    【解决方案2】:

    根据提到的 Jon Skeet 文章,添加静态构造函数将导致此实现是线程安全的:

    只有当类型没有使用名为 beforefieldinit 的特殊标志标记时,.NET 才能保证类型初始值设定项的惰性。不幸的是,C# 编译器(至少在 .NET 1.1 运行时中提供)将所有没有静态构造函数的类型(即看起来像构造函数但标记为静态的块)标记为 beforefieldinit

    (见http://csharpindepth.com/articles/general/singleton.aspx#cctor

    正如你现在所拥有的,它不是线程安全的。如果你做这样的事情,它就会变成线程安全的:

    public class MyClass
    {
        /// <summary>
        /// Get singleton instance of this class
        /// </summary>
        public static readonly MyClass Instance = new MyClass();
    
        static MyClass()
        {
            //causes the compiler to not mark this as beforefieldinit, giving this thread safety
            //for accessing the singleton.
        }
    
        //.. the rest of your stuff..
    }
    

    【讨论】:

    • 我以为 beforefieldinit 只是控制类型初始化器的惰性。它不应该影响线程安全吗?
    • @Morpheus:没错。原始示例是线程安全的,但可能不会延迟初始化。就个人而言,如果惰性初始化对我很重要,我会使用 Lazy 类来实现单例(我认为 Skeet 的文章早于该类,除非他在最初编写它后对其进行了更新)。
    • 延迟初始化对我来说并不重要。所以撇开这一点不谈,问题中发布的代码有什么问题吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多