kat*_*roh 8 .net c# singleton multithreading
这不是一个更简单,更安全(因此更好)的方法来实现单例而不是双重检查锁定mambo-jambo?这种方法有什么缺点吗?
public class Singleton
{
private static Singleton _instance;
private Singleton() { Console.WriteLine("Instance created"); }
public static Singleton Instance
{
get
{
if (_instance == null)
{
Interlocked.CompareExchange(ref _instance, new Singleton(), null);
}
return _instance;
}
}
public void DoStuff() { }
}
Run Code Online (Sandbox Code Playgroud)
编辑:线程安全测试失败,谁能解释为什么?为什么Interlocked.CompareExchange不是真正的原子?
public class Program
{
static void Main(string[] args)
{
Parallel.For(0, 1000000, delegate(int i) { Singleton.Instance.DoStuff(); });
}
}
Result (4 cores, 4 logical processors)
Instance created
Instance created
Instance created
Instance created
Instance created
Run Code Online (Sandbox Code Playgroud)
Bli*_*ndy 10
如果您的单身人士有多次初始化自身的危险,那么您的问题会更严重.为什么不使用:
public class Singleton
{
private static Singleton instance=new Singleton();
private Singleton() {}
public static Singleton Instance{get{return instance;}}
}
Run Code Online (Sandbox Code Playgroud)
在初始化方面绝对是线程安全的.
编辑:如果我不清楚,你的代码是非常错误的.无论是if检查和new被不线程安全的!你需要使用一个合适的单例类.
您可能正在创建多个实例,但这些实例将被垃圾收集,因为它们不会在任何地方使用.在任何情况下,静态_instance字段变量都不会多次更改其值,即从null变为有效值的单次时间.因此,尽管已经创建了多个实例,但此代码的使用者将只看到相同的实例.
锁定免费编程
Joe Duffy在他的书" Windows上的并发编程"实际上分析了你试图在第10章"内存模型和锁定自由"中使用的这种模式,第526页.
他将此模式称为轻松引用的Lazy初始化:
public class LazyInitRelaxedRef<T> where T : class
{
private volatile T m_value;
private Func<T> m_factory;
public LazyInitRelaxedRef(Func<T> factory) { m_factory = factory; }
public T Value
{
get
{
if (m_value == null)
Interlocked.CompareExchange(ref m_value, m_factory(), null);
return m_value;
}
}
/// <summary>
/// An alternative version of the above Value accessor that disposes
/// of garbage if it loses the race to publish a new value. (Page 527.)
/// </summary>
public T ValueWithDisposalOfGarbage
{
get
{
if (m_value == null)
{
T obj = m_factory();
if (Interlocked.CompareExchange(ref m_value, obj, null) != null && obj is IDisposable)
((IDisposable)obj).Dispose();
}
return m_value;
}
}
}
Run Code Online (Sandbox Code Playgroud)
正如我们所看到的,在上面的示例中,方法是免费锁定的,其代价是创建一次性对象.在任何情况下,Value属性都不会因此类API的使用者而改变.
权衡利弊
锁定自由需要付出代价,并且需要谨慎选择权衡.在这种情况下,锁定自由的代价是您必须创建不会使用的对象实例.这可能是一个可接受的支付价格,因为你知道通过免于锁定,死锁风险和线程争用的风险较低.
然而,在这个特定的实例中,单例的语义本质上是创建一个对象的单个实例,所以我更愿意选择Lazy<T>@Centro在他的答案中引用.
尽管如此,它仍然存在问题,我们应该何时使用Interlocked.CompareExchange?我喜欢你的例子,这是非常发人深省的,很多人很快就把它误解为错误,因为@Blindy引用并不是非常错误.
这一切都归结为你是否计算了权衡并决定:
只要你意识到权衡取舍并让它有意识地决定创建新对象以获得无锁,那么你的例子也可以是一个可以接受的答案.
| 归档时间: |
|
| 查看次数: |
4864 次 |
| 最近记录: |