当前线程中的单例

时间:2014-03-28 17:32:17

标签: c# design-patterns

我的单身如下:

public class CurrentSingleton
{
    private static CurrentSingleton uniqueInstance = null;
    private static object syncRoot = new Object();
    private CurrentSingleton() { }

    public static CurrentSingleton getInstance()
    {
        if (uniqueInstance == null)
        {
            lock (syncRoot)
            {
                if (uniqueInstance == null)
                    uniqueInstance = new CurrentSingleton();
            }
        }
        return uniqueInstance;
    }
}

我想检查,如果我有两个线程,是否有两个不同的单身人士?我想,我将有两个不同的单身人士(有不同的参考资料),所以我正在做的事情:

class Program
{
    static void Main(string[] args)
    {
        int currentCounter = 0;
        for (int i = 0; i < 100; i++)
        {
            cs1 = null;
            cs2 = null;

            Thread ct1 = new Thread(cfun1);
            Thread ct2 = new Thread(cfun2);
            ct1.Start();
            ct2.Start(); 
            if (cs1 == cs2) currentCounter++;
        }
        Console.WriteLine(currentCounter);
        Console.Read();

    }

    static CurrentSingleton cs1;
    static CurrentSingleton cs2;

    static void cfun1()
    {
        cs1 = CurrentSingleton.getInstance();
    }

    static void cfun2()
    {
        cs2 = CurrentSingleton.getInstance();
    }
}

我想我应该得到currentCounter = 0(在这种情况下,每两个单身是不同的 - 因为是由其他的线程创建的)。不幸的是,我得到了例如currentCounter = 70所以在70个案例中我有相同的单身人士...你能告诉我为什么吗?

4 个答案:

答案 0 :(得分:13)

  

我想检查,如果我有两个线程,是否有两个不同的单身人士

不,没有。每个整个static共享一个AppDomain字段,而不是每个帖子。

如果你想为每个线程分别设置值,我建议使用ThreadLocal<T>来存储支持数据,因为这将为每个线程数据提供一个很好的包装器。

此外,在C#中,通常最好通过Lazy<T>实现延迟单例,而不是通过双重检查锁定。这看起来像是:

public sealed class CurrentSingleton // Seal your singletons if possible
{
    private static Lazy<CurrentSingleton> uniqueInstance = new Lazy<CurrentSingleton>(() => new CurrentSingleton());
    private CurrentSingleton() { }

    public static CurrentSingleton Instance  // use a property, since this is C#...
    {
        get { return uniqueInstance.Value; }
    }
}

要创建一个为每个线程提供一个实例的类,可以使用:

public sealed class InstancePerThread
{
    private static ThreadLocal<InstancePerThread> instances = new ThreadLocal<InstancePerThread>(() => new InstancePerThread());

    private InstancePerThread() {}
    public static InstancePerThread Instance
    {
        get { return instances.Value; }
    }
}

答案 1 :(得分:4)

默认情况下,static字段是访问它的所有线程共享的单个实例。

你应该看一下[ThreadStatic] attribute。将其应用于static字段,使其具有访问它的每个线程的不同实例。

答案 2 :(得分:0)

使用锁定对象可确保只创建一个值;您可以通过在CurrentSingleton构造函数中添加一些日志记录来验证这一点。

但是,我认为你的逻辑中存在一个小差距:想象两个线程同时调用此方法,而uniqueInstance为空。两者都将评估= null子句,并进入锁定状态。一个人将赢,锁定syncRoot,并初始化uniqueInstance。当lock块结束时,另一个将获得自己的锁定,并再次初始化uniqueInstance

在测试syncRoot是否为空之前,您需要锁定uniqueInstance

答案 3 :(得分:0)

无论你做什么,你永远不会得到 currentCounter = 0 。 因为我们忘记了 application / C#code 也在某个线程中运行的事实,并且C#设置了一些优先级来运行代码。如果通过在Main方法和CurrentSingleton中设置断点来调试代码,您会注意到这一点。当您到达并为CurrentSingleton创建新的Object时,for循环可以是迭代3或4或任何数字。迭代很快,代码比较空值和Object或Object和null值。我认为这就是问题所在。

Reed有点静态将始终共享,因此您需要以下列方式更改代码

  public class CurrentSingleton
  {
    [ThreadStatic]
    private static CurrentSingleton uniqueInstance = null;
    private static object syncRoot = new Object();
    private CurrentSingleton() { }

    public static CurrentSingleton getInstance()
    {
        if (uniqueInstance == null)
            uniqueInstance = new CurrentSingleton();

        return uniqueInstance;
    }
}

根据分析,你在第70次迭代时得到两个不同的对象但是,这就是错配可能是null,Object或Object和null。要成功获得两个不同的对象,您需要使用[ThreadStatic]