代码之家  ›  专栏  ›  技术社区  ›  Mykhailo Seniutovych

非线程安全的单例有什么危险?

  •  0
  • Mykhailo Seniutovych  · 技术社区  · 7 年前

    最近我发现 this article 这就解释了如何实现 thread safe singleton pattern 在C语言中。 然而,我想知道让您的单例线程安全有多重要,让您的单例类非线程安全有什么危险和什么危险。考虑一个非常简单、非线程安全的单例模式实现:

    public class ThreadNotSafeSingleton
    {
        public static ThreadNotSafeSingleton Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new ThreadNotSafeSingleton();
                }
    
                return _instance;
            }
        }
    
        private static ThreadNotSafeSingleton _instance;
    
        private ThreadNotSafeSingleton()
        {
        }
    }
    

    现在让我们假设,由于这段代码是非线程安全的,两个线程将进入这一行代码 _instance = new ThreadNotSafeSingleton(); . 在这种情况下,第一个线程将初始化 _instance 字段,然后第二个字段将初始化 _实例 再一次,不管怎样,你会有一个 _实例 存在于应用程序中。

    那么这里有什么大不了的?我知道,如果您有更复杂的代码,并且您的构造函数运行一些其他可能不安全的代码,那么这可能是危险的。但是,如果您的singleton如此简单,那么非线程安全是否存在任何问题?

    1 回复  |  直到 7 年前
        1
  •  3
  •   BionicCode    7 年前

    由于单例模式仅将类限制为一个实例,因此您的方法违反了该模式。

    如果出于任何原因,这对于您的实现来说都是可以的,并且我们遵循您所述的示例,那么这两个并发访问中的每一个都很有可能获得 ThreadNotSafeSingleton . 如果编译器决定不需要读取 _instance 返回前刚写入的变量。这种优化行为是由C实现的内存模型定义的。

    这个 volatile 关键字经常被引用作为可能的解决方案,但它不能解决同步问题(如 BionicCode ,当线程1通过 if (_instance == null) 行并进入睡眠状态,然后线程2也会对相同的if进行评估,并开始声明该singleton。当线程1稍后唤醒时,它将通知另一个单例。

        2
  •  3
  •   Kazem Javadi    7 年前

    单例模式的目的是只从一个类型创建一个对象。

    想象 thread1 thread2 来到 get 的访问器 Instance 财产 第一次 ( _instance 仍然是 null ) 同时 :

    螺纹1 检查 if (_instance == null) 表达并看到 _实例 无效的 以及 同时 , 螺纹2 检查 if 表达并看到 _实例 无效的 但也一样。 螺纹1 到这里来 _instance = new ThreadNotSafeSingleton(); 表达式并从 ThreadNotSafeSingleton 类型。因为 螺纹2 检查 如果 表达并看到 _实例 无效的 也是,来 如果 代码块并创建一个新对象,该对象将覆盖 _实例 变量。最后, 螺纹1 使用不同于 螺纹2 的对象。

    这个问题发生在 _实例 无效的 两个或多个线程试图同时获取您类型的实例。您必须限制同时访问 如果 线程的代码体(线程同步),特别是当 _instace 无效的 然而。

    推荐文章