C# 当前线程中的单例
我的单身生活如下:C# 当前线程中的单例,c#,design-patterns,C#,Design Patterns,我的单身生活如下: public class CurrentSingleton { private static CurrentSingleton uniqueInstance = null; private static object syncRoot = new Object(); private CurrentSingleton() { } public static CurrentSingleton getInstance() {
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();
}
}
类程序
{
静态void Main(字符串[]参数)
{
int currentCounter=0;
对于(int i=0;i<100;i++)
{
cs1=null;
cs2=null;
螺纹ct1=新螺纹(cfun1);
螺纹ct2=新螺纹(cfun2);
ct1.Start();
ct2.Start();
如果(cs1==cs2)currentCounter++;
}
控制台写入线(当前计数器);
Console.Read();
}
静态电流单态cs1;
静态电流单态cs2;
静态无效cfun1()
{
cs1=CurrentSingleton.getInstance();
}
静态无效cfun2()
{
cs2=CurrentSingleton.getInstance();
}
}
我想我应该得到
currentCounter=0
(在这种情况下,每两个单例都是不同的,因为它们是由其他threrad创建的)。不幸的是,例如,我得到了currentCounter=70
,所以在70种情况下,我有相同的单例。。。您能告诉我为什么吗?默认情况下,静态
字段是由访问它的所有线程共享的单个实例
你应该看一下这张照片。将其应用于静态
字段,使访问它的每个线程都有一个不同的实例
我想检查一下,如果我有两个线程,是否有两个不同的单线程
不,没有。static
字段在整个AppDomain
中共享,而不是在每个线程中共享
如果希望每个线程有单独的值,我建议使用来存储备份数据,因为这将为每个线程数据提供一个很好的包装器
另外,在C#中,通常最好通过lazy
实现一个懒惰的单例,而不是通过双重检查锁定。这看起来像:
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 CurrentSingleton//如果可能,请密封您的单例
{
private static Lazy uniqueInstance=new Lazy(()=>new CurrentSingleton());
私有CurrentSingleton(){}
公共静态CurrentSingleton实例//使用属性,因为这是C#。。。
{
获取{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; }
}
}
公共密封类InstancePerThread
{
private static ThreadLocal instances=new ThreadLocal(()=>new InstancePerThread());
私有InstancePerThread(){}
公共静态InstancePerThread实例
{
获取{return instances.Value;}
}
}
使用锁定对象可确保只创建一个值;您可以通过在CurrentSingleton
构造函数中添加一些日志来验证这一点
然而,我认为在您的逻辑中有一个小缺口:假设两个线程同时调用此方法,而uniqueInstance
为空。两者都将计算=null
子句,并前进到锁定。一个将获胜,锁定syncRoot
,并初始化uniqueInstance
。当lock
块结束时,另一个将获得自己的锁,并再次初始化uniqueInstance
在测试
uniqueInstance
是否为空之前,您需要锁定syncRoot
。无论您做什么,您都永远不会得到currentCounter=0。
因为我们忘记了一个事实,application/C#code也在某个线程中运行,并且C#设置了一些运行代码的优先级。如果您通过在Main方法和CurrentSingleton中放置断点来调试代码,您将注意到这一点。当您到达并为CurrentSingleton创建新对象时,for循环可能是迭代3或4或任何数字。迭代速度很快,代码正在比较空值和对象或对象和空值。我想这就是问题所在
has got point static将始终共享,因此您需要按照以下方式更改代码
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]我不知道
ThreadLocal
。使用它可能比使用[ThreadStatic]
@TimothyShields更好是的,它是一个更好的抽象。您可以自己清理,并且初始化要好得多。@Reed Copsey使用您的类I have currentCounter=13或currentCounter=18。所以在某些情况下,我们仍然有相同的例子。。。这不好,不是吗?@makcis这仍然会使用相同的实例。正如我所说的,如果您想避免使用相同的实例,您需要使用ThreadLocal
。[ThreadStatic]
可以工作,但要注意存在一些奇怪的初始化问题(字段初始值设定项只在访问的第一个线程上工作),如果您需要处理实例,则没有干净的方法来处理清理。我补充道[线程静态]在变量static CurrentSingleton cs1;和static CurrentSingleton cs2;之前。现在我得到currentCounter=100,所以在每种情况下我都有相同的实例…它仍然是wrong@makcis您需要在CurrentSingleton类中使用它,而不是您正在设置的变量。在锁中有一个单独的检查,因此它实际上可以在(microsoft CLR的实现)的.NET内存模型。诚然,理论上它需要内存屏障才能正确,但双重检查锁定“基本上”是可以的。使用[ThreadStatic]时不需要锁定
,因为两个线程不可能在该资源上存在资源争用…我更正了代码。我只是在调试时将我的分析结果放入其中。如果我错了,请更正。您只需