首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >单例模式混淆

单例模式混淆
EN

Stack Overflow用户
提问于 2011-12-25 18:33:36
回答 6查看 284关注 0票数 1

根据单例模式,

代码语言:javascript
复制
public sealed class Singleton
{
    static Singleton instance=null;

    Singleton()
    {
    }

    public void abc(){
    }

    public static Singleton Instance
    {
        get
        {
            if (instance==null)
            {
                instance = new Singleton();
            }
            return instance;
        }
    }
}

上面的代码不是线程安全的,两个不同的线程可能都评估了测试if (instance==null)并发现它是真的,然后都创建了实例,这违反了单例模式。

混淆是实例是静态的,当它在UI线程或其他线程上被调用时,它怎么会是空的?

编辑

我的意思是,一旦我调用了Singleton.Instance.abc();在手动释放Singleton.Instance之前,它不应该为空。对吗?

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2011-12-25 18:44:59

将控制权传递给ThreadA

ThreadA尝试获取Instance,结果发现是null

将控制权传递给ThreadB

ThreadB尝试获取Instance,结果发现是null

将控制权传递给ThreadA

ThreadA实例化了Instance

将控制权传递给ThreadB

ThreadB重新实例化了Instance

解决方案:你可以使用一个static构造函数来确保这种情况不会发生。

票数 5
EN

Stack Overflow用户

发布于 2011-12-25 18:35:32

你是对的,你显示的单例不是线程安全的。使其线程安全的简单方法是:

代码语言:javascript
复制
public sealed class Singleton
{
    static Singleton instance=null;
    static lockObject = new object();
    Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            lock(lockObject)
            {
               if (instance==null)
               {
                   instance = new Singleton();
               }
            }
            return instance;
        }
    }
}

利用C#使用静态( http://www.yoda.arachsys.com/csharp/singleton.html )的方式,您可以编写一个没有锁的线程安全版本

代码语言:javascript
复制
public sealed class Singleton
{
    static readonly Singleton instance=new Singleton();

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

    Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            return instance;
        }
    }
}

最后一个版本利用了这样一个事实,即C#中的静态构造函数被指定为仅在创建类的实例或引用静态成员时执行,每个AppDomain执行一次。

票数 2
EN

Stack Overflow用户

发布于 2011-12-25 18:40:15

编辑:

我的意思是,一旦我调用了Singleton.Instance.abc();在手动释放Singleton.Instance之前,它不应该为空。对吗?

一旦单个线程被用来访问单例,它就已经被初始化了,并且在调用之后启动的任何线程都可以安全地访问单例,而不会发生null

如果你同时启动多个线程来访问Singleton,并且让你的代码保持原样,你就会有一个问题。如果不使用线程同步(lock)来确保每次只输入一个线程的if null块,那么就会出现竞争条件。

如果多个线程偷偷经过if块,那么多个线程将尝试初始化instance。这很混乱,很难说到底会发生什么。一个问题是,您实际上可能会获得多个实例,每个偷偷通过的线程一个实例。任何后续调用(在竞态条件之后)都将获得创建的最后一个实例。

不过,这可能不是您将看到的最糟糕的问题。可能还有更糟糕的问题导致崩溃(不确定)。最好对Singleton使用线程安全操作,而不是找出:)

关于如何让你的Singleton线程安全,请看下面我的原始答案:

您可以在静态构造函数中创建实例。

静态构造函数只会在第一次访问时被调用,因此您不需要惰性初始化代码。

代码语言:javascript
复制
public sealed class Singleton
{
    private static Singleton instance;

    private Singleton() { }

    static Singleton()
    {
        instance = new Singleton();
    }

    public static Singleton Instance
    {
        get { return instance; }
    }
}

根据the answer to this question的说法,它保证只被调用一次,并且是线程安全的:

在创建类的任何实例或访问任何静态成员之前,保证每个应用程序域只运行一次

静态构造函数。http://msdn.microsoft.com/en-us/library/aa645612.aspx

所示的实现对于初始构造是线程安全的,也就是说,构造Singleton对象不需要锁定或空测试。然而,这并不意味着实例的任何使用都将被同步。有很多种方法可以做到这一点;我已经在下面展示了一种。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8629229

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档