我正在尝试为xml站点地图文件实现自己的提供程序。因此,我已经开始通过反射器调查默认的Microsoft XmlSiteMapProvider,我发现了一个令我困惑的片段,在这里:
SiteMapNode node = this._siteMapNode;
if (node != null)
{
return node;
}
XmlDocument configDocument = this.GetConfigDocument();
lock (base._lock)
{
**if (this._siteMapNode == null)**
{...// more code
好的,首先我们检查节点是否为空,然后,当检查结束时,我们再次查看它。这个If语句不是多余的吗?或者它可能与锁有关?
答案 0 :(得分:3)
是的,它与锁相关。可能是在第一次空检查和锁定之间,另一个线程执行相同的代码并创建了节点,因此当第一个线程获得锁定时,它不再为null。当您有延迟加载静态资源时,请务必记住这一点:
private static List<string> _someStringList;
private static object _lock = new object();
public static List<string> SomeStringList
{
get
{
if (_someStringList == null)
{
// if more than one thread try to do this at the same time
// it may be that the other thread has already gotten the lock
// and is creating the object at this point
lock (_lock)
{
// now we have the lock, so we check again to make
// sure that another thread did not get here first
if (_someStringList == null)
{
// now we know for sure that the object is not yet created, and
// also cannot have been created since we have the lock
_someStringList = new List<string>();
}
}
}
return _someStringList;
}
}
执行此操作时,建议您用于锁定的对象不用于任何其他目的,但是为此目的创建它。
答案 1 :(得分:2)
是的,检查两次null是Double-Checked Locking。检查null,获取锁,然后再次检查null。这个想法是减少锁定开销,因为如果第一个空检查通过,该对象已经初始化,并且没有理由再次执行锁定/初始化步骤。您需要进行第二次空检查,因为该对象可能已由第一次空检查和获取锁定之间的其他线程初始化。
答案 2 :(得分:1)
想象一下,如果多个线程几乎同时在这段代码中运行会发生什么。
两个线程都可能达到第一个空检查并决定创建一个新节点。锁定是为了确保只有一个线程进入第二个空块并获得创建新节点。
您可能会问:为什么不在第一次空检查之前锁定?原因是,在node
不 null的情况下,您希望代码尽可能快,特别是在运行多个线程的情况下,因此您只能锁定对象,如果您希望它仍然为空,然后你在锁内确认。