易失性关键字和锁定实例属性

时间:2011-01-31 19:38:47

标签: c#

我有一些控制器类的实例属性的代码,如下所示:

public class Controller
{
    private static volatile Controller _instance;
    private static object syncRoot = new Object();

    private Controller() { }

    public static Controller Instance
    {
        get
        {
            if (_instance == null)
            {
                lock (syncRoot)
                {
                    if (_instance == null)
                        _instance = new Controller();
                }
            }

            return _instance;
        }
    }

    public void Start()
    {

    }
}

在阅读msdn docs on the volatile keyword之后,我不确定第二次空检查是否多余以及编写getter的更好方法是这样的:

get
{            
    lock (syncRoot)
    {
        if (_instance == null)
            _instance = new Controller();
    }            

    return _instance;
}

这两种实现中哪一种更适合多线程性能和DRY'(冗余删除)?

4 个答案:

答案 0 :(得分:13)

这称为“双重检查锁定”模式。这是一种低锁优化的尝试,因此非常危险。

无法保证模式在CLR v1.0上正常运行。是否在后期版本中如此保证是一个有争议的问题;有些文章说是,有些人说不。这非常令人困惑。

我会完全避免它,除非你有充分的理由认为锁定解决方案不足以满足你的需求。我会使用像Joe Duffy这样的专家编写的更高级原语,比如Lazy<T>。他们更可能是正确的。

此问题与

重复

The need for volatile modifier in double checked locking in .NET

有关详细信息,请参阅详细解答。特别是,如果您打算编写任何低锁代码,您绝对必须阅读Vance的文章:

http://msdn.microsoft.com/en-us/magazine/cc163715.aspx

和Joe的文章:

http://www.bluebytesoftware.com/blog/PermaLink,guid,543d89ad-8d57-4a51-b7c9-a821e3992bf6.aspx

请注意,Vance的文章声称双重检查锁定可以保证在CLR v2中起作用。我不清楚文章中讨论的保证是否实际在CLR v2中实现;我从来没有从任何人那里得到一个直接的答案,并且已经听说过它们都是按照规定实施的。再一次,当你自己做这种低锁的东西时,你在没有网的空中飞人;如果可能的话,请避免使用它。

答案 1 :(得分:7)

更好的选择是使用Lazy<T>

private static readonly Lazy<Controller> _instance = new Lazy<Controller>
                                               (() => new Controller());
private Controller() { }

public static Controller Instance
{
    get
    {
        return _instance.Value;
    }
}

但是,如果您遇到.NET 4之前的版本,我建议您阅读Jon Skeet's article on Singletons in C#。它讨论了上述技术的优点和缺点,并为.NET 3.5及更早版本提供了更好的实例化懒惰实例。

答案 2 :(得分:0)

执行此操作的经典方法是double-checked locking:在获取锁之前检查一次以减少开销(获取锁相对较贵) - 然后在获得锁之后再次检查以确保它不是已经设定了。

编辑:正如所指出的那样,Lazy<T>是一个更好的选择,我在这里留下这个答案是为了完整。

if (_instance == null)
{
    lock (syncRoot)
    {
        if (_instance == null)
            _instance = new Controller();
    }            
}

return _instance;

答案 3 :(得分:0)

要在锁定对象之前检查,以免发生不必要的锁定 你还想在锁定后检查以避免对象的多次实例化

第一个不是必需的,但出于性能考虑是非常可取的