我的单身如下:
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个案例中我有相同的单身人士...你能告诉我为什么吗?
答案 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]