<?xml version="1.0" encoding="gb2312"?>

<!-- RSS generated by oioj.net on 4/16/2004 ; 感谢LeXRus提供 RSS 2.0 文档; 此文件可自由使用，但请保留此行信息 --> 
<!-- Source download URL: http://blogger.org.cn/blog/rss2.asp       -->
<rss version="2.0">

<channel>
<title>newlulu的博客</title>
<link>http://blogger.org.cn/blog/blog.asp?name=newlulu</link>
<description>newlulu的博客</description>
<copyright>blogger.org.cn</copyright>
<generator>W3CHINA Blog</generator>
<webMaster>webmaster@blogger.org.cn</webMaster>
<item>
<title><![CDATA[锁无关的数据结构与Hazard指针]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=newlulu&amp;id=11483</link>
<author>newlulu</author>
<pubDate>2006/2/1 18:40:53</pubDate>
<description><![CDATA[
<p style="text-align: left;" class="MsoNormal" align="left"><em><span style="font-size: 12pt; font-family: &quot;Palatino Linotype&quot;;" lang="EN-US">C/C++ Users Journal</span></em><span style="font-size: 12pt; font-family: &quot;Palatino Linotype&quot;;" lang="EN-US"> December, 2004<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><strong><span style="font-size: 24pt; font-family: 宋体;">锁无关的数据结构与</span></strong><strong><span style="font-size: 24pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Hazard</span></strong><strong><span style="font-size: 24pt; font-family: 宋体;">指针</span></strong><strong><span style="font-size: 24pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></strong></p>

<p style="text-align: left;" class="MsoNormal" align="left"><strong><span style="font-size: 18pt; font-family: 宋体;">操纵有限的资源</span></strong><strong><span style="font-size: 18pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></strong></p>

<p style="text-align: left;" class="MsoNormal" align="left"><strong><span style="font-size: 13.5pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p>&nbsp;</o:p></span></strong></p>

<p style="text-align: left;" class="MsoNormal" align="left"><strong><span style="font-size: 13.5pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">By Andrei Alexandrescu and Maged Michael<o:p></o:p></span></strong></p>

<p style="text-align: left;" class="MsoNormal" align="left"><strong><span style="font-size: 13.5pt; font-family: 宋体; color: rgb(255, 102, 0);">刘未鹏</span></strong><strong><span style="font-size: 13.5pt; font-family: &quot;Lucida Console&quot;; color: rgb(255, 102, 0);" lang="EN-US">(pp_liu@msn.com) </span></strong><strong><span style="font-size: 13.5pt; font-family: 宋体; color: rgb(255, 102, 0);">译</span></strong><strong><span style="font-size: 13.5pt; font-family: &quot;Lucida Console&quot;; color: rgb(255, 102, 0);" lang="EN-US"><o:p></o:p></span></strong></p>

<p style="text-align: left;" class="MsoNormal" align="left"><strong><span style="font-size: 13.5pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p>&nbsp;</o:p></span></strong></p>

<p style="text-align: left;" class="MsoNormal" align="left"><em><span style="font-size: 12pt; font-family: &quot;Palatino Linotype&quot;;" lang="EN-US">Andrei Alexandrescu</span></em><span style="font-size: 12pt; font-family: 宋体;">是华盛顿大学计算机科学系的在读研究生，也是<em>《</em></span><em><span style="font-size: 12pt; font-family: &quot;Palatino Linotype&quot;;" lang="EN-US">Modern C++ Design</span></em><em><span style="font-size: 12pt; font-family: 宋体;">》</span></em><span style="font-size: 12pt; font-family: 宋体;">一书的作者。他的邮箱是</span><span style="font-size: 12pt; font-family: &quot;Palatino Linotype&quot;;"> <span lang="EN-US"><a href="mailto:andrei@metalanguage.com">andrei@metalanguage.com</a></span></span><span style="font-size: 12pt; font-family: 宋体;">。</span><span style="font-size: 12pt; font-family: &quot;Palatino Linotype&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Palatino Linotype&quot;;" lang="EN-US">Maged Michael</span><span style="font-size: 12pt; font-family: 宋体;">是</span><span style="font-size: 12pt; font-family: &quot;Palatino Linotype&quot;;" lang="EN-US">IBM</span><span style="font-size: 12pt; font-family: 宋体;">的</span><span style="font-size: 12pt; font-family: &quot;Palatino Linotype&quot;;" lang="EN-US">Thomas J.Watson</span><span style="font-size: 12pt; font-family: 宋体;">研究中心的研究员。</span><span style="font-size: 12pt; font-family: &quot;Palatino Linotype&quot;;" lang="EN-US"><o:p></o:p></span></p>

<div style="text-align: center;" class="MsoNormal" align="center"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">  <hr align="center" size="2" width="100%">  </span></div>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">首先，我很荣幸向大家介绍</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Maged Michael</span><span style="font-size: 12pt; font-family: 宋体;">，</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Maged Michael</span><span style="font-size: 12pt; font-family: 宋体;">和我共同撰写了本期的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Generic&lt;Programming&gt;</span><span style="font-size: 12pt; font-family: 宋体;">，他是锁无关（</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Lock-Free</span><span style="font-size: 12pt; font-family: 宋体;">）算法方面的权威，而且对一些困难问题</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">[3]</span><span style="font-size: 12pt; font-family: 宋体;">提出了令人惊讶的独创性方案。在你将要阅读的这篇文章中我们就会向你呈上这么一项激动人心的技术。下文将要介绍的算法通过操纵手头有限的资源，以一种近乎神奇的方式达到了其目的。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">如果把</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Generic&lt;Programming&gt;</span><span style="font-size: 12pt; font-family: 宋体;">栏目比作肥皂剧的话，这一集中那些反派角色将会被轰走（译注：很遗憾，这位</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">C++</span><span style="font-size: 12pt; font-family: 宋体;">大师的幽默水平似乎很</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">crappy</span><span style="font-size: 12pt; font-family: 宋体;">，每当看到这类</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">joke</span><span style="font-size: 12pt; font-family: 宋体;">我都只能无语）。为了帮你回忆一下这里所谓的“反派角色”是谁，我们回顾一下上期的文章</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">[1]</span><span style="font-size: 12pt; font-family: 宋体;">。（下面我先帮你做一个简要回顾，但再后面的内容要求你对上期的文章至少做过一遍粗略的阅读，并且大致知道我们要去解决的问题是什么。）</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">上期的文章中，在引入了锁无关编程的概念之后我们曾实现了一个锁无关的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">WRRM</span><span style="font-size: 12pt; font-family: 宋体;">（</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Write-Rarely-Read-Many</span><span style="font-size: 12pt; font-family: 宋体;">）</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Map</span><span style="font-size: 12pt; font-family: 宋体;">。我们经常看到这类数据结构以全局表、工厂对象、高速缓存</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">(cache)</span><span style="font-size: 12pt; font-family: 宋体;">等形式出现。我们遇到的问题在于内存的回收：既然我们的数据结构得是锁无关的，那么我们该如何来回收内存呢？正如前面讨论过的，这是个棘手的问题，因为锁无关意味着任何线程在任何时候都有不受限的机会去操作任何对象。为了解决这一问题，上篇文章中曾作了以下几点尝试：</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<ul type="disc">
<li style="text-align: left;" class="MsoNormal"><span style="font-size: 12pt; font-family: 宋体;">对</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">使用引用计数。该策略是注定要失败的，因为它要求对指针和引用计数变量（它们在内存中处于不同的地方）同时进行（原子地）更新。尽管有少数论文使用了</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">DCAS(Double Compare-And-Swap</span><span style="font-size: 12pt; font-family: 宋体;">，该指令的行为正是这里所需要的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">)</span><span style="font-size: 12pt; font-family: 宋体;">，然而这个东西从来也没能流行起来，因为没有它我们照样能够做许多事情，而且它也没有强大到能够高效地实现出任意事务的程度。参考</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">[2]</span><span style="font-size: 12pt; font-family: 宋体;">关于</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">DCAS</span><span style="font-size: 12pt; font-family: 宋体;">的用途的讨论。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></li><li style="text-align: left;" class="MsoNormal"><span style="font-size: 12pt; font-family: 宋体;">等待并</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete</span><span style="font-size: 12pt; font-family: 宋体;">。一旦“清理”线程知道某个指针要被</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete</span><span style="font-size: 12pt; font-family: 宋体;">掉了，它就可以等待一段“足够长的时间”然后</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete</span><span style="font-size: 12pt; font-family: 宋体;">它。然而问题是这段“足够长的时间”到底应该有多长呢？所以说这个方案听上去跟“分配一块足够大的缓冲区”一样蹩脚，后者怎么样大家是知道的。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></li><li style="text-align: left;" class="MsoNormal"><span style="font-size: 12pt; font-family: 宋体;">将引用计数跟指针紧挨着存放在一起。这个方案使用了要求较少，可以合理实现的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">CAS2</span><span style="font-size: 12pt; font-family: 宋体;">原语，</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">CAS2</span><span style="font-size: 12pt; font-family: 宋体;">能够原子地</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Compare-and-Swap</span><span style="font-size: 12pt; font-family: 宋体;">内存中的两个紧邻的字。大多数</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">32</span><span style="font-size: 12pt; font-family: 宋体;">位机器都支持这一原语，只不过支持它的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">64</span><span style="font-size: 12pt; font-family: 宋体;">位机器倒没那么多了，不过对于后一种情况我们可以在指针内的位上面做点文章来解决这个问题）。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></li>
</ul>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">上面提到的最后一个方案被证明是可行的，只不过它却将我们的漂亮的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">WRRM Map</span><span style="font-size: 12pt; font-family: 宋体;">变成了一个粗陋的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">WRRMBNTM(Write-Rarely-Read-Many-But-Not-Too-Many) Map</span><span style="font-size: 12pt; font-family: 宋体;">，因为其写操作需要等到绝对没有任何读操作正在进行的时候才会去写。这就意味着，只要有一个读操作在所有其它读操作结束之前开始了，写操作就只能干等。而这就不能算是锁无关了。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><em><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Bullet</span></em><em><span style="font-size: 12pt; font-family: 宋体;">探长走进了他的债务人的办公室，坐下，点燃一枝雪茄，以一种绝对冷静的、足以将整个沸腾的办公室冰冻住的声音说道：“除非拿回我的钱，否则我是不会走的。”</span></em><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">基于引用计数的一个可行方案是将引用计数变量跟指针分离开来。这样写操作就不再被读操作所阻塞了，不过它们仍只能在引用计数降为</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">0</span><span style="font-size: 12pt; font-family: 宋体;">时才能去</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete</span><span style="font-size: 12pt; font-family: 宋体;">旧的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map[5]</span><span style="font-size: 12pt; font-family: 宋体;">。此外，如果采用此方案，我们就只需使用针对单字的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">CAS</span><span style="font-size: 12pt; font-family: 宋体;">，这就使我们的技术可移植于不同的硬件架构之间，包括那些不支持</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">CAS2</span><span style="font-size: 12pt; font-family: 宋体;">的。然而，该方案仍存在问题：即我们并没有消除等待，而只不过是将它延期了，这便增加了内存占用方面的问题发生的几率。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><em><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Bullet</span></em><em><span style="font-size: 12pt; font-family: 宋体;">探长怯怯地敲开他的债务人的办公室的门，吞吞吐吐的问：“您现在可以还我的</span></em><em><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">19,990</span></em><em><span style="font-size: 12pt; font-family: 宋体;">块钱了吗？没有？噢，好吧，好吧。这里是我仅剩的</span></em><em><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">10</span></em><em><span style="font-size: 12pt; font-family: 宋体;">块钱，给。过些时日我再过来，看看你有没有我的</span></em><em><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">20,000</span></em><em><span style="font-size: 12pt; font-family: 宋体;">块钱。”</span></em><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">在这种方案下，写函数可能会保留任意多个未被回收的旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">，并眼巴巴的等着（可能会一直等下去）它们的引用计数变成</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">0</span><span style="font-size: 12pt; font-family: 宋体;">。换句话说，即使仅有一个读线程被延迟也可能会导致任意量的内存无法被回收，而且该线程拖得越久，情况就越遭。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">我们真正需要的是这么一种机制，基于它，读线程可以告诉写线程别在它们的眼皮子底下回收某些旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">，同时读线程还不能强迫写线程在那等着释放一堆也许是任意数量的旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">。实际上，存在着一个不仅是锁无关、而且还是等待无关的解决方案。（回忆一下上期文章中的定义：锁无关意味着系统中总存在某个线程能够得以继续执行；而等待无关则是一个更强的条件，它意味着所有线程都能往下进行。）而且该方案并不要求用到什么特殊的操作，如</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">DCAS</span><span style="font-size: 12pt; font-family: 宋体;">、</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">CAS2</span><span style="font-size: 12pt; font-family: 宋体;">之类，它只需使用我们可以信赖的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">CAS</span><span style="font-size: 12pt; font-family: 宋体;">原语即可。开始感到有点意思了吧？那就继续往下读。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><strong><span style="font-size: 13.5pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Hazard</span></strong><strong><span style="font-size: 13.5pt; font-family: 宋体;">指针</span></strong><strong><span style="font-size: 13.5pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></strong></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">回顾一下上期文章中的代码，我们有一个模板的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">WRRMMap</span><span style="font-size: 12pt; font-family: 宋体;">，它内部有一个指向某个经典的单线程</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Map</span><span style="font-size: 12pt; font-family: 宋体;">对象（如</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">std::map</span><span style="font-size: 12pt; font-family: 宋体;">）的指针，同时它还向外界提供了一个多线程的、锁无关的访问接口：</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">template &lt;class K, class V&gt;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">class WRRMMap {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>Map&lt;K, V&gt; * pMap_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>...<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">};<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">每当</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">WRRMMap</span><span style="font-size: 12pt; font-family: 宋体;">需要更新的时候，想要对它进行更新的线程就会首先创建它的一个完整的副本，更新副本，然而再将</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">指向该副本，最后</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete</span><span style="font-size: 12pt; font-family: 宋体;">掉</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">原先所指的旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">。我们认为这种做法算不上低效，因为</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">WRRMMap</span><span style="font-size: 12pt; font-family: 宋体;">通常只是被读取，很少被更新。然而，麻烦的问题是我们怎样才能妥善地将旧的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">销毁掉，因为任何时候都可能有其它线程正在对它们进行读取。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针就是我们所采用的技术，借助于</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针，线程就得以安全高效地将它们的内存使用告知所有其它线程。每一读线程都拥有一个“单个写线程</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">/</span><span style="font-size: 12pt; font-family: 宋体;">多个读线程”的共享指针，这便是所谓的“</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针”。当一个读线程将一个</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">的地址赋给它的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针时，即意味着它在向其它（写）线程宣称：“我正在读该</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">，如果你原意，你可以将它替换掉，但别改动它的内容，当然更不要去</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete</span><span style="font-size: 12pt; font-family: 宋体;">它。”</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">而站在写线程的立场来看，写线程在</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete</span><span style="font-size: 12pt; font-family: 宋体;">任何被替换下来的旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">之前须得检查读线程的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针（看看该旧的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">是否仍在被使用）。也就是说，如果一个写线程是在一个（或多个）读线程已将某个特定</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">挂到它（们）的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针上之后才将该</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">替换下来的，那么该写线程就只有等到那些读线程的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针被</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">release</span><span style="font-size: 12pt; font-family: 宋体;">之后才能去</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete</span><span style="font-size: 12pt; font-family: 宋体;">被替换掉的旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">每当一个写线程替换某个</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">的时候，它会将替换下来的旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">放入一个私有链表中。直到该链表中的旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">达到一定数量的时候（待会我们会讨论这个数量的上限是如何选取的），写线程就会扫描读线程的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针，看看旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">列表中有哪些是与这些</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针匹配的。如果某个旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">不与任何读线程的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针匹配，那么销毁该</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">就是安全的。否则写线程就会仍将其保留在链表中，直到下次扫描。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">下面就是我们将要使用的数据结构。主要的共享结构是一个关于</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针（</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">HPRecType</span><span style="font-size: 12pt; font-family: 宋体;">）的单链表，由</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pHead_</span><span style="font-size: 12pt; font-family: 宋体;">所指向。该链表中的每一项都包含一个</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针（</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pHazard_</span><span style="font-size: 12pt; font-family: 宋体;">）、一个标志着该</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针是否正处于使用中的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">flag</span><span style="font-size: 12pt; font-family: 宋体;">（</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">active_</span><span style="font-size: 12pt; font-family: 宋体;">）、以及一个指向下一节点的指针（</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pNext_</span><span style="font-size: 12pt; font-family: 宋体;">）。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">HPRecType</span><span style="font-size: 12pt; font-family: 宋体;">类提供了两个原语：</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Acquire</span><span style="font-size: 12pt; font-family: 宋体;">和</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Release</span><span style="font-size: 12pt; font-family: 宋体;">。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">HPRecType::Acquire</span><span style="font-size: 12pt; font-family: 宋体;">返回一个指向一个</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">HPRecType</span><span style="font-size: 12pt; font-family: 宋体;">节点的指针，姑且称其为</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">p</span><span style="font-size: 12pt; font-family: 宋体;">。如此一来，获取了该指针的线程就可以设置</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">p-&gt;pHazard_</span><span style="font-size: 12pt; font-family: 宋体;">，并以此确保其它线程会小心对待该指针。而当该线程不再使用该指针时，它就会调用</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">HPRecType::Release(p)</span><span style="font-size: 12pt; font-family: 宋体;">。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">// Hazard pointer record<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">class HPRecType {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>HPRecType * pNext_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>int active_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// Global header of the HP list<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>static HPRecType * pHead_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// The length of the list<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>static int listLen_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">public:<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// Can be used by the thread<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;</span><span style="">&nbsp; </span>// that acquired it<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>void * pHazard_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>static HPRecType * Head() {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return pHead_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// Acquires one hazard pointer<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>static HPRecType * Acquire() {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// Try to reuse a retired HP record<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>HPRecType * p = pHead_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>for (; p; p = p-&gt;pNext_) {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if (p-&gt;active_ ||<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>!CAS(&amp;p-&gt;active_, 0, 1))<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>continue;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// Got one!<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return p;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// Increment the list length<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int oldLen;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>do {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>oldLen = listLen_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>} while (!CAS(&amp;listLen_, oldLen,<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>oldLen + 1));<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// Allocate a new one<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>HPRecType * p = new HPRecType;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>p-&gt;active_ = 1;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>p-&gt;pHazard_ = 0;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// Push it to the front<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>do {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>old = pHead_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>p-&gt;pNext_ = old;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>} while (!CAS(&amp;pHead_, old, p));<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return p;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// Releases a hazard pointer<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>static void Release(HPRecType* p) {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>p-&gt;pHazard_ = 0;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>p-&gt;active_ = 0;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">};<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">// Per-thread private variable<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">__per_thread__ vector&lt;Map&lt;K, V&gt; *&gt; rlist;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">每个线程都拥有一个“退役链表”（在我们的实现中，这实际上就是一个</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">vector&lt;Map&lt;K,V&gt;*&gt;</span><span style="font-size: 12pt; font-family: 宋体;">），“退役链表”是一个容器，负责存放该线程不再需要的、一旦其它线程也不再使用它们就可以被</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete</span><span style="font-size: 12pt; font-family: 宋体;">掉的指针。“退役链表”的访问不需要被同步，因为它位于每线程存储区中；永远都只有一个线程（即拥有它的线程）能访问它。我们使用</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">__per_thread__</span><span style="font-size: 12pt; font-family: 宋体;">修饰符来省却分配线程相关存储的麻烦。通过这一办法，每当一个线程想要销毁旧的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">时，它就只需要调用</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Retire</span><span style="font-size: 12pt; font-family: 宋体;">函数即可。（注意，正如上期文章中提到的，为了简单起见，我们并未在代码中插入内存屏障。）</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">template &lt;class K, class V&gt;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">class WRRMMap {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>Map&lt;K, V&gt; * pMap_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>...<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">private:<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>static void Retire(Map&lt;K, V&gt; * pOld) {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// put it in the retired list<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>rlist.push_back(pOld);<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>if (rlist.size() &gt;= R) {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Scan(HPRecType::Head());<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">};<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">毫无隐瞒，就这些！现在，我们的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Scan</span><span style="font-size: 12pt; font-family: 宋体;">函数会进行一个差集计算的算法，即当前线程的“退役链表”作为一个集合，其它所有线程的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针作为一个集合，求它们两个的差集。那么，这个差集的意义又是什么呢？让我们停下来考虑一下：前一个集合是当前线程所认为没有用的旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">指针，而后一集合则代表那些正被某个或某几个线程使用着的旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">指针（即那些线程的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针所指的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">）但是后者相当于“濒死”指针！根据“退役链表”和</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针的定义，如果一个指针“退役”了并且没有被任何一个线程的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针所指（使用）的话，该指针所指的旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">就是可以被安全地销毁的。换言之，第一个集合与第二个集合的差集正是那些可被安全销毁的旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">的指针。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><strong><span style="font-size: 13.5pt; font-family: 宋体;">主算法</span></strong><strong><span style="font-size: 13.5pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></strong></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">OK</span><span style="font-size: 12pt; font-family: 宋体;">，现在就让我们来看看如何实现</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Scan</span><span style="font-size: 12pt; font-family: 宋体;">算法，以及它能够提供什么样的保证。在进行</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Scan</span><span style="font-size: 12pt; font-family: 宋体;">时，我们需要对</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">rlist</span><span style="font-size: 12pt; font-family: 宋体;">（退役链表）跟</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pHead</span><span style="font-size: 12pt; font-family: 宋体;">（</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针链表）所代表的两个集合作差集运算。换言之：“对于退役链表中的每个指针，检查它是否同样位于</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针链表（</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pHead</span><span style="font-size: 12pt; font-family: 宋体;">）中，如果不是，则它属于差集，即可被安全销毁。”此外，为了对该算法加以优化，我们可以在进行查找之前先对</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针链表进行排序，然后对每个退役指针在其中进行二分查找。下面就是</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Scan</span><span style="font-size: 12pt; font-family: 宋体;">的实现：</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">void Scan(HPRecType * head) {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// Stage 1: Scan hazard pointers list<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// collecting all non-null ptrs<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>vector&lt;void*&gt; hp;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>while (head) {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void * p = head-&gt;pHazard_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if (p) hp.push_back(p);<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>head = head-&gt;pNext_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// Stage 2: sort the hazard pointers<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>sort(hp.begin(), hp.end(),<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>less&lt;void*&gt;());<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// Stage 3: Search for'em!<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>vector&lt;Map&lt;K, V&gt;*&gt;::iterator i =<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>rlist.begin();<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>while (i != rlist.end()) {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if (!binary_search(hp.begin(),<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>hp.end(),<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>*i) {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// Aha!<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>delete *i;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if (&amp;*i != &amp;rlist.back()) {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>*i = rlist.back();<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>rlist.pop_back();<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>} else {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>++i;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">最后一个循环做了实际的工作。其中运用了一点小小的技巧来避免</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">rlist vector</span><span style="font-size: 12pt; font-family: 宋体;">的重排：在</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete</span><span style="font-size: 12pt; font-family: 宋体;">了</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">rlist</span><span style="font-size: 12pt; font-family: 宋体;">中的一个可被删除的指针之后，我们将那个指针的位置用</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">rlist</span><span style="font-size: 12pt; font-family: 宋体;">的最后一个元素来填充，然后再将实际的最后一个元素</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pop</span><span style="font-size: 12pt; font-family: 宋体;">掉。这样做是允许的，因为</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">rlist</span><span style="font-size: 12pt; font-family: 宋体;">中的元素并不要求有序。此外我们使用了</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">C++</span><span style="font-size: 12pt; font-family: 宋体;">标准库函数</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">sort</span><span style="font-size: 12pt; font-family: 宋体;">跟</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">binary_search</span><span style="font-size: 12pt; font-family: 宋体;">。不过你可以将</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">vector</span><span style="font-size: 12pt; font-family: 宋体;">换成你所喜欢的容器，如哈希表这类易于搜索的数据结构。一个均衡的哈希表的期望查找时间是常数，而且建造这样一个哈希表也很容易，因为它是完全私有的，而且在组织它之前你知道其中所有的值。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">那么，</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Scan</span><span style="font-size: 12pt; font-family: 宋体;">的效率又如何呢？首先，注意到这整个算法是等待无关的（正如上文所介绍的那样）：每个线程的执行时间都不依赖于其它任何线程的行为。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">其次，</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">rlist</span><span style="font-size: 12pt; font-family: 宋体;">的平均大小是一个任意值，记为</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">R</span><span style="font-size: 12pt; font-family: 宋体;">，我们将该值用作触发</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Scan</span><span style="font-size: 12pt; font-family: 宋体;">的</span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">阈值（见上文代码中的</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">WRRMMap&lt;K,V&gt;::Replace</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">函数）。如果我们将</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">指针存放在一个哈希表中（而不是原先用的有序</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">vector</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">），那么</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Scan</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">算法的期望复杂度就可以降为</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">O(R)</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">。最后，已退役但尚存活在系统中（未被</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">）的旧</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">的最大数目是</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">N*R</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">，其中</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">N</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">为写线程的数目（译注：因为只有写线程才会将旧</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">替换下来，而每个写线程都有一个私有的</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">rlist</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">退役链表，其最大长度为</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">R</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">）。至于</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">R</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">的具体值，一个良好的选择是</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">(1+K)H</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">，其中</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">H</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">为</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">指针的数目（即我们的代码中的</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">listLen</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">，在我们的例子中这也就是读线程的数目（因为每个读线程有且仅有一个</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Hazard</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">指针）），而</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">K</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">则是某个小的正常数，如</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">1/4</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">。因此</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">R</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">大于</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">H</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">且与</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">H</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">成正比。这样一来，每次</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Scan</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">都能保证</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">掉</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">R-H</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">（即</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">O(R)</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">）个节点（旧</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">），而如果我们使用的是哈希表的话，时间复杂度就是</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">O(R)</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">。因此，确定一个节点是否可被安全销毁的分摊时间复杂度为常数。</span></span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><strong><span style="font-size: 13.5pt; font-family: 宋体;">缝合</span></strong><strong><span style="font-size: 13.5pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">WRRMMap<o:p></o:p></span></strong></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">下面我们就来把</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针缝合进</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">WRRMMap</span><span style="font-size: 12pt; font-family: 宋体;">的原语，即</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Lookup</span><span style="font-size: 12pt; font-family: 宋体;">和</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Update</span><span style="font-size: 12pt; font-family: 宋体;">中。对于写线程（执行</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Update</span><span style="font-size: 12pt; font-family: 宋体;">的线程）来说，它们所需要做的就是在正常情况下应当</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete pMap_</span><span style="font-size: 12pt; font-family: 宋体;">的地方改成调用</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">WRRMMap&lt;K,V&gt;::Retire</span><span style="font-size: 12pt; font-family: 宋体;">。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">void Update(const K&amp;k, const V&amp;v){<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>Map&lt;K, V&gt; * pNew = 0;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>do {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Map&lt;K, V&gt; * pOld = pMap_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>delete pNew;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pNew = new Map&lt;K, V&gt;(*pOld);<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>(*pNew)[k] = v;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>} while (!CAS(&amp;pMap_, pOld, pNew));<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>Retire(pOld);<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">读线程首先得通过</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">HPRecType::Acquire</span><span style="font-size: 12pt; font-family: 宋体;">获得一个</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针，并将其赋为</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">，以便写线程可以查找到。当读线程使用完毕该指针之后，就通过</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">HPRecType::Release</span><span style="font-size: 12pt; font-family: 宋体;">来释放对应的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">V Lookup(const K&amp;k){<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>HPRecType * pRec = HPRecType::Acquire();<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>Map&lt;K, V&gt; * ptr;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>do {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ptr = pMap_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pRec-&gt;pHazard_ = ptr;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>} while (pMap_ != ptr);<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// Save Willy<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>V result = (*ptr)[k];<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// pRec can be released now<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// because it's not used anymore<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>HPRecType::Release(pRec);<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>return result;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">在上面的代码中，为什么读线程需要重新检查</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">是否等于</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">ptr</span><span style="font-size: 12pt; font-family: 宋体;">呢（</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">while(pMap_!=ptr)</span><span style="font-size: 12pt; font-family: 宋体;">）？考虑下面这种情况：</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">原先指向</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map m</span><span style="font-size: 12pt; font-family: 宋体;">，读线程将</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">ptr</span><span style="font-size: 12pt; font-family: 宋体;">赋为</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">，也就是</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">&amp;m</span><span style="font-size: 12pt; font-family: 宋体;">，接着读线程就进入了休眠（此时它还未来得及将</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针指向</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">m</span><span style="font-size: 12pt; font-family: 宋体;">）。而就在它的休眠期间，一个写线程偷偷溜进来，更新了</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">，然后将</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">原先所指的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">（即</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">m</span><span style="font-size: 12pt; font-family: 宋体;">）放到了退役链表中，并紧接着检查各线程的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针，由于刚才的读线程在进入休眠时还未来得及设置</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针，故</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">m</span><span style="font-size: 12pt; font-family: 宋体;">被认为是可以销毁的旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">，于是被写线程销毁。而这时读线程醒来了，并接着未完成的工作，将</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针赋为</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">ptr(</span><span style="font-size: 12pt; font-family: 宋体;">即</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">&amp;m)</span><span style="font-size: 12pt; font-family: 宋体;">。如果是这样的话，一旦读线程接下来对</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">ptr</span><span style="font-size: 12pt; font-family: 宋体;">进行解引用，它就会读取到被破坏的数据或访问了未被映射的内存。以上就是读线程需要再次检查</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">的原因。如果结果发现</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">并不等于</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">&amp;m</span><span style="font-size: 12pt; font-family: 宋体;">的话，读线程就无法确定将</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">m</span><span style="font-size: 12pt; font-family: 宋体;">退役掉的写线程是否看到了它的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针被置为</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">&amp;m</span><span style="font-size: 12pt; font-family: 宋体;">（换句话说，是否已将</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">m</span><span style="font-size: 12pt; font-family: 宋体;">销毁）。所以此时读线程继续对</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">m</span><span style="font-size: 12pt; font-family: 宋体;">进行读取就是不安全的，所以读线程需要从头来过。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">如果读线程而后发现</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">的确指向</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">m</span><span style="font-size: 12pt; font-family: 宋体;">，那么此时从</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">m</span><span style="font-size: 12pt; font-family: 宋体;">读取就是安全的。那么，这是否意味着在两次读取</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">之间，</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">没有被改动过呢？不一定，在这期间</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">m</span><span style="font-size: 12pt; font-family: 宋体;">可能被移除并再次挂到</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">上（一次或多次），但这并不要紧。要紧的是在第二次读取的时候</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">m</span><span style="font-size: 12pt; font-family: 宋体;">挂在</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">上，而此时读线程的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针也已经指向它了。因此从这个点以后（知道</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针被改变），访问</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">m</span><span style="font-size: 12pt; font-family: 宋体;">都是安全的。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">这样</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Lookup</span><span style="font-size: 12pt; font-family: 宋体;">和</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Update</span><span style="font-size: 12pt; font-family: 宋体;">就都成了锁无关的（不过，仍不是等待无关的）：读线程不会阻塞写线程，多个读线程之间也不会互相撞车（这一点是引用计数方案所做不到的）。所以，我们得到了一个完美的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">WRRM Map</span><span style="font-size: 12pt; font-family: 宋体;">：读取操作非常快，而且不会互相冲突，更新操作仍然很快，而且能够保证整体进展。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">如果我们想要</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Lookup</span><span style="font-size: 12pt; font-family: 宋体;">成为等待无关的，我们可以使用“陷阱”（</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">trap</span><span style="font-size: 12pt; font-family: 宋体;">）技术</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">[4]</span><span style="font-size: 12pt; font-family: 宋体;">，它是基于</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针的一门技术。在前面的代码中，当读线程将其</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针赋为</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">ptr</span><span style="font-size: 12pt; font-family: 宋体;">时，它其实是想捕获某个特定的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">（</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">*ptr</span><span style="font-size: 12pt; font-family: 宋体;">）。而使用陷阱技术，读线程就可以设置一个“陷阱”，该“陷阱”保证能够捕获住某个有效的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">，这样</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Lookup</span><span style="font-size: 12pt; font-family: 宋体;">就成了等待无关的算法，同样，如果我们有自动垃圾收集机制的话，</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Lookup</span><span style="font-size: 12pt; font-family: 宋体;">也是等待无关的（参考专栏上一期的文章）。要想了解“陷阱”技术的细节，可参卡</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">[4]</span><span style="font-size: 12pt; font-family: 宋体;">。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><strong><span style="font-size: 13.5pt; font-family: 宋体;">泛化</span></strong><strong><span style="font-size: 13.5pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></strong></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">到这里为止，我们几乎已经完成了一个坚实的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">设计。不过还有一些事情需要考虑，以便让你对该技术形成一个全面的认识。完整的参考见</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">[3]</span><span style="font-size: 12pt; font-family: 宋体;">。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">我们已经知道如何才能共享一个</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">，然而倘若我们想要共享的是许多个对象呢？答案很简单：首先，我们的算法可以自然的推广到每线程多个</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针的情况。不过，通常在任何特定的时刻，一个线程需要同时保护的指针数目是非常有限的。此外，</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针可以被“重载”（因为它们的类型是</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">void*</span><span style="font-size: 12pt; font-family: 宋体;">）：一个线程可以将同一个</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针用在任何数目的数据结构身上，只要满足同一时刻</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针只被用于其中某一个数据结构这一条件即可。大多数时候，每线程拥有一个或两个</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针对于整个程序来说就已经足够了。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">最后注意一下，</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Lookup</span><span style="font-size: 12pt; font-family: 宋体;">在完成了一次</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">lookup</span><span style="font-size: 12pt; font-family: 宋体;">之后立即调用了</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">HPRecType::Release</span><span style="font-size: 12pt; font-family: 宋体;">。在一个考虑性能因素的应用程序中，我们可以只获取</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针一次，并用它来进行多次</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">lookup</span><span style="font-size: 12pt; font-family: 宋体;">，最后才释放它（一次）。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><strong><span style="font-size: 13.5pt; font-family: 宋体;">结论</span></strong><strong><span style="font-size: 13.5pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></strong></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">长
久以来人们一直试图解决锁无关算法的内存释放问题，有时人们甚至觉得对于这个问题并不存在令人满意的答案。然而，实际上我们只需作一点小小的辅助性工作，
同时在线程私有跟线程共享数据之间小心操作，我们就能够创造出一种在速度和内存占用方面皆给出了强大且令人满意的保证的算法。此外，尽管我们通篇使用了</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">WRRMMap</span><span style="font-size: 12pt; font-family: 宋体;">作为例子，但</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针技术实际上能够被运用到复杂得多的数据结构上。对于那些大小可以任意扩充和缩小的动态数据结构来说内存回收问题则更显重要；例如，设想一个拥有上千个链表、其中每个链表的大小都可能增长为上百万个节点的程序，这种场合正是</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针展现其强大身手的地方。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">一个读线程所能干的最糟糕的事情就是在未能将它的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针置为空的情况下就中止了，这会导致它的每个</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针所指向的对象永远也不能被释放。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><em><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Bullet</span></em><em><span style="font-size: 12pt; font-family: 宋体;">探长冲进了他的债务人的办公室，并立即认识到他今天是不可能拿回他的钱了。于是他立即说道：“伙计，你已经上了我的黑名单了。日后我会再来拜访你，除非你死了。而且就算你‘不幸’故去，你欠的债也将不会超过</span></em><em><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">100</span></em><em><span style="font-size: 12pt; font-family: 宋体;">块。再见！”</span></em><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><strong><span style="font-size: 13.5pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">References<o:p></o:p></span></strong></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">[1] Alexandrescu, Andrei. "Generic&lt;Programming&gt;: Lock-Free Data Structures," <em>C++ Users Journal</em>, October 2004. <o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">[2]
Doherty, Simon, David L. Detlefs, Lindsay Grove, Christine H. Flood,
Victor Luchangco, Paul A. Martin, Mark Moir, Nir Shavit, and Jr. Guy L.
Steele. "DCAS is not a silver bullet for nonblocking algorithm design."
<em>Proceedings of the Sixteenth Annual ACM Symposium on Parallelism in Algorithms and Architectures</em>, pages 216-224. ACM Press, 2004. ISBN 1-58113-840-7. <o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">[3] Michael, Maged M. "Hazard Pointers: Safe Memory Reclamation for Lock-Free Objects," <em>IEEE Transactions on Parallel and Distributed Systems</em>, pages 491-504. IEEE, 2004. <o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">[4] Michael, Maged M. "Practical Lock-Free and Wait-Free LL/SC/VL Implementations Using 64-Bit CAS," <em>Proceedings of the 18th International Conference on Distributed Computing</em>, LNCS volume 3274, pages 144-158, October 2004. <o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">[5]
Tang, Hong, Kai Shen, and Tao Yang. "Program Transformation and Runtime
Support for Threaded MPI Execution on Shared-memory Machines," <em>ACM Transactions on Programming Languages and Systems</em>, 22(4):673-700, 2000 (<a href="http://citeseer.ist.psu.edu/tang99program.html">citeseer.ist.psu.edu/tang99program.html</a>). <o:p></o:p></span></p>

<p class="MsoNormal"><span style="font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>
]]></description>
</item><item>
<title><![CDATA[锁无关的数据结构与Hazard指针]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=newlulu&amp;id=11482</link>
<author>newlulu</author>
<pubDate>2006/2/1 18:40:53</pubDate>
<description><![CDATA[
<p style="text-align: left;" class="MsoNormal" align="left"><em><span style="font-size: 12pt; font-family: &quot;Palatino Linotype&quot;;" lang="EN-US">C/C++ Users Journal</span></em><span style="font-size: 12pt; font-family: &quot;Palatino Linotype&quot;;" lang="EN-US"> December, 2004<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><strong><span style="font-size: 24pt; font-family: 宋体;">锁无关的数据结构与</span></strong><strong><span style="font-size: 24pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Hazard</span></strong><strong><span style="font-size: 24pt; font-family: 宋体;">指针</span></strong><strong><span style="font-size: 24pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></strong></p>

<p style="text-align: left;" class="MsoNormal" align="left"><strong><span style="font-size: 18pt; font-family: 宋体;">操纵有限的资源</span></strong><strong><span style="font-size: 18pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></strong></p>

<p style="text-align: left;" class="MsoNormal" align="left"><strong><span style="font-size: 13.5pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p>&nbsp;</o:p></span></strong></p>

<p style="text-align: left;" class="MsoNormal" align="left"><strong><span style="font-size: 13.5pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">By Andrei Alexandrescu and Maged Michael<o:p></o:p></span></strong></p>

<p style="text-align: left;" class="MsoNormal" align="left"><strong><span style="font-size: 13.5pt; font-family: 宋体; color: rgb(255, 102, 0);">刘未鹏</span></strong><strong><span style="font-size: 13.5pt; font-family: &quot;Lucida Console&quot;; color: rgb(255, 102, 0);" lang="EN-US">(pp_liu@msn.com) </span></strong><strong><span style="font-size: 13.5pt; font-family: 宋体; color: rgb(255, 102, 0);">译</span></strong><strong><span style="font-size: 13.5pt; font-family: &quot;Lucida Console&quot;; color: rgb(255, 102, 0);" lang="EN-US"><o:p></o:p></span></strong></p>

<p style="text-align: left;" class="MsoNormal" align="left"><strong><span style="font-size: 13.5pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p>&nbsp;</o:p></span></strong></p>

<p style="text-align: left;" class="MsoNormal" align="left"><em><span style="font-size: 12pt; font-family: &quot;Palatino Linotype&quot;;" lang="EN-US">Andrei Alexandrescu</span></em><span style="font-size: 12pt; font-family: 宋体;">是华盛顿大学计算机科学系的在读研究生，也是<em>《</em></span><em><span style="font-size: 12pt; font-family: &quot;Palatino Linotype&quot;;" lang="EN-US">Modern C++ Design</span></em><em><span style="font-size: 12pt; font-family: 宋体;">》</span></em><span style="font-size: 12pt; font-family: 宋体;">一书的作者。他的邮箱是</span><span style="font-size: 12pt; font-family: &quot;Palatino Linotype&quot;;"> <span lang="EN-US"><a href="mailto:andrei@metalanguage.com">andrei@metalanguage.com</a></span></span><span style="font-size: 12pt; font-family: 宋体;">。</span><span style="font-size: 12pt; font-family: &quot;Palatino Linotype&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Palatino Linotype&quot;;" lang="EN-US">Maged Michael</span><span style="font-size: 12pt; font-family: 宋体;">是</span><span style="font-size: 12pt; font-family: &quot;Palatino Linotype&quot;;" lang="EN-US">IBM</span><span style="font-size: 12pt; font-family: 宋体;">的</span><span style="font-size: 12pt; font-family: &quot;Palatino Linotype&quot;;" lang="EN-US">Thomas J.Watson</span><span style="font-size: 12pt; font-family: 宋体;">研究中心的研究员。</span><span style="font-size: 12pt; font-family: &quot;Palatino Linotype&quot;;" lang="EN-US"><o:p></o:p></span></p>

<div style="text-align: center;" class="MsoNormal" align="center"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">  <hr align="center" size="2" width="100%">  </span></div>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">首先，我很荣幸向大家介绍</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Maged Michael</span><span style="font-size: 12pt; font-family: 宋体;">，</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Maged Michael</span><span style="font-size: 12pt; font-family: 宋体;">和我共同撰写了本期的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Generic&lt;Programming&gt;</span><span style="font-size: 12pt; font-family: 宋体;">，他是锁无关（</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Lock-Free</span><span style="font-size: 12pt; font-family: 宋体;">）算法方面的权威，而且对一些困难问题</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">[3]</span><span style="font-size: 12pt; font-family: 宋体;">提出了令人惊讶的独创性方案。在你将要阅读的这篇文章中我们就会向你呈上这么一项激动人心的技术。下文将要介绍的算法通过操纵手头有限的资源，以一种近乎神奇的方式达到了其目的。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">如果把</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Generic&lt;Programming&gt;</span><span style="font-size: 12pt; font-family: 宋体;">栏目比作肥皂剧的话，这一集中那些反派角色将会被轰走（译注：很遗憾，这位</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">C++</span><span style="font-size: 12pt; font-family: 宋体;">大师的幽默水平似乎很</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">crappy</span><span style="font-size: 12pt; font-family: 宋体;">，每当看到这类</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">joke</span><span style="font-size: 12pt; font-family: 宋体;">我都只能无语）。为了帮你回忆一下这里所谓的“反派角色”是谁，我们回顾一下上期的文章</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">[1]</span><span style="font-size: 12pt; font-family: 宋体;">。（下面我先帮你做一个简要回顾，但再后面的内容要求你对上期的文章至少做过一遍粗略的阅读，并且大致知道我们要去解决的问题是什么。）</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">上期的文章中，在引入了锁无关编程的概念之后我们曾实现了一个锁无关的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">WRRM</span><span style="font-size: 12pt; font-family: 宋体;">（</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Write-Rarely-Read-Many</span><span style="font-size: 12pt; font-family: 宋体;">）</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Map</span><span style="font-size: 12pt; font-family: 宋体;">。我们经常看到这类数据结构以全局表、工厂对象、高速缓存</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">(cache)</span><span style="font-size: 12pt; font-family: 宋体;">等形式出现。我们遇到的问题在于内存的回收：既然我们的数据结构得是锁无关的，那么我们该如何来回收内存呢？正如前面讨论过的，这是个棘手的问题，因为锁无关意味着任何线程在任何时候都有不受限的机会去操作任何对象。为了解决这一问题，上篇文章中曾作了以下几点尝试：</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<ul type="disc">
<li style="text-align: left;" class="MsoNormal"><span style="font-size: 12pt; font-family: 宋体;">对</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">使用引用计数。该策略是注定要失败的，因为它要求对指针和引用计数变量（它们在内存中处于不同的地方）同时进行（原子地）更新。尽管有少数论文使用了</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">DCAS(Double Compare-And-Swap</span><span style="font-size: 12pt; font-family: 宋体;">，该指令的行为正是这里所需要的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">)</span><span style="font-size: 12pt; font-family: 宋体;">，然而这个东西从来也没能流行起来，因为没有它我们照样能够做许多事情，而且它也没有强大到能够高效地实现出任意事务的程度。参考</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">[2]</span><span style="font-size: 12pt; font-family: 宋体;">关于</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">DCAS</span><span style="font-size: 12pt; font-family: 宋体;">的用途的讨论。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></li><li style="text-align: left;" class="MsoNormal"><span style="font-size: 12pt; font-family: 宋体;">等待并</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete</span><span style="font-size: 12pt; font-family: 宋体;">。一旦“清理”线程知道某个指针要被</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete</span><span style="font-size: 12pt; font-family: 宋体;">掉了，它就可以等待一段“足够长的时间”然后</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete</span><span style="font-size: 12pt; font-family: 宋体;">它。然而问题是这段“足够长的时间”到底应该有多长呢？所以说这个方案听上去跟“分配一块足够大的缓冲区”一样蹩脚，后者怎么样大家是知道的。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></li><li style="text-align: left;" class="MsoNormal"><span style="font-size: 12pt; font-family: 宋体;">将引用计数跟指针紧挨着存放在一起。这个方案使用了要求较少，可以合理实现的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">CAS2</span><span style="font-size: 12pt; font-family: 宋体;">原语，</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">CAS2</span><span style="font-size: 12pt; font-family: 宋体;">能够原子地</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Compare-and-Swap</span><span style="font-size: 12pt; font-family: 宋体;">内存中的两个紧邻的字。大多数</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">32</span><span style="font-size: 12pt; font-family: 宋体;">位机器都支持这一原语，只不过支持它的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">64</span><span style="font-size: 12pt; font-family: 宋体;">位机器倒没那么多了，不过对于后一种情况我们可以在指针内的位上面做点文章来解决这个问题）。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></li>
</ul>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">上面提到的最后一个方案被证明是可行的，只不过它却将我们的漂亮的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">WRRM Map</span><span style="font-size: 12pt; font-family: 宋体;">变成了一个粗陋的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">WRRMBNTM(Write-Rarely-Read-Many-But-Not-Too-Many) Map</span><span style="font-size: 12pt; font-family: 宋体;">，因为其写操作需要等到绝对没有任何读操作正在进行的时候才会去写。这就意味着，只要有一个读操作在所有其它读操作结束之前开始了，写操作就只能干等。而这就不能算是锁无关了。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><em><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Bullet</span></em><em><span style="font-size: 12pt; font-family: 宋体;">探长走进了他的债务人的办公室，坐下，点燃一枝雪茄，以一种绝对冷静的、足以将整个沸腾的办公室冰冻住的声音说道：“除非拿回我的钱，否则我是不会走的。”</span></em><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">基于引用计数的一个可行方案是将引用计数变量跟指针分离开来。这样写操作就不再被读操作所阻塞了，不过它们仍只能在引用计数降为</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">0</span><span style="font-size: 12pt; font-family: 宋体;">时才能去</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete</span><span style="font-size: 12pt; font-family: 宋体;">旧的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map[5]</span><span style="font-size: 12pt; font-family: 宋体;">。此外，如果采用此方案，我们就只需使用针对单字的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">CAS</span><span style="font-size: 12pt; font-family: 宋体;">，这就使我们的技术可移植于不同的硬件架构之间，包括那些不支持</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">CAS2</span><span style="font-size: 12pt; font-family: 宋体;">的。然而，该方案仍存在问题：即我们并没有消除等待，而只不过是将它延期了，这便增加了内存占用方面的问题发生的几率。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><em><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Bullet</span></em><em><span style="font-size: 12pt; font-family: 宋体;">探长怯怯地敲开他的债务人的办公室的门，吞吞吐吐的问：“您现在可以还我的</span></em><em><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">19,990</span></em><em><span style="font-size: 12pt; font-family: 宋体;">块钱了吗？没有？噢，好吧，好吧。这里是我仅剩的</span></em><em><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">10</span></em><em><span style="font-size: 12pt; font-family: 宋体;">块钱，给。过些时日我再过来，看看你有没有我的</span></em><em><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">20,000</span></em><em><span style="font-size: 12pt; font-family: 宋体;">块钱。”</span></em><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">在这种方案下，写函数可能会保留任意多个未被回收的旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">，并眼巴巴的等着（可能会一直等下去）它们的引用计数变成</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">0</span><span style="font-size: 12pt; font-family: 宋体;">。换句话说，即使仅有一个读线程被延迟也可能会导致任意量的内存无法被回收，而且该线程拖得越久，情况就越遭。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">我们真正需要的是这么一种机制，基于它，读线程可以告诉写线程别在它们的眼皮子底下回收某些旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">，同时读线程还不能强迫写线程在那等着释放一堆也许是任意数量的旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">。实际上，存在着一个不仅是锁无关、而且还是等待无关的解决方案。（回忆一下上期文章中的定义：锁无关意味着系统中总存在某个线程能够得以继续执行；而等待无关则是一个更强的条件，它意味着所有线程都能往下进行。）而且该方案并不要求用到什么特殊的操作，如</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">DCAS</span><span style="font-size: 12pt; font-family: 宋体;">、</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">CAS2</span><span style="font-size: 12pt; font-family: 宋体;">之类，它只需使用我们可以信赖的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">CAS</span><span style="font-size: 12pt; font-family: 宋体;">原语即可。开始感到有点意思了吧？那就继续往下读。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><strong><span style="font-size: 13.5pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Hazard</span></strong><strong><span style="font-size: 13.5pt; font-family: 宋体;">指针</span></strong><strong><span style="font-size: 13.5pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></strong></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">回顾一下上期文章中的代码，我们有一个模板的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">WRRMMap</span><span style="font-size: 12pt; font-family: 宋体;">，它内部有一个指向某个经典的单线程</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Map</span><span style="font-size: 12pt; font-family: 宋体;">对象（如</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">std::map</span><span style="font-size: 12pt; font-family: 宋体;">）的指针，同时它还向外界提供了一个多线程的、锁无关的访问接口：</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">template &lt;class K, class V&gt;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">class WRRMMap {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>Map&lt;K, V&gt; * pMap_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>...<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">};<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">每当</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">WRRMMap</span><span style="font-size: 12pt; font-family: 宋体;">需要更新的时候，想要对它进行更新的线程就会首先创建它的一个完整的副本，更新副本，然而再将</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">指向该副本，最后</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete</span><span style="font-size: 12pt; font-family: 宋体;">掉</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">原先所指的旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">。我们认为这种做法算不上低效，因为</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">WRRMMap</span><span style="font-size: 12pt; font-family: 宋体;">通常只是被读取，很少被更新。然而，麻烦的问题是我们怎样才能妥善地将旧的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">销毁掉，因为任何时候都可能有其它线程正在对它们进行读取。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针就是我们所采用的技术，借助于</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针，线程就得以安全高效地将它们的内存使用告知所有其它线程。每一读线程都拥有一个“单个写线程</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">/</span><span style="font-size: 12pt; font-family: 宋体;">多个读线程”的共享指针，这便是所谓的“</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针”。当一个读线程将一个</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">的地址赋给它的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针时，即意味着它在向其它（写）线程宣称：“我正在读该</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">，如果你原意，你可以将它替换掉，但别改动它的内容，当然更不要去</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete</span><span style="font-size: 12pt; font-family: 宋体;">它。”</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">而站在写线程的立场来看，写线程在</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete</span><span style="font-size: 12pt; font-family: 宋体;">任何被替换下来的旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">之前须得检查读线程的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针（看看该旧的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">是否仍在被使用）。也就是说，如果一个写线程是在一个（或多个）读线程已将某个特定</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">挂到它（们）的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针上之后才将该</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">替换下来的，那么该写线程就只有等到那些读线程的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针被</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">release</span><span style="font-size: 12pt; font-family: 宋体;">之后才能去</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete</span><span style="font-size: 12pt; font-family: 宋体;">被替换掉的旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">每当一个写线程替换某个</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">的时候，它会将替换下来的旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">放入一个私有链表中。直到该链表中的旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">达到一定数量的时候（待会我们会讨论这个数量的上限是如何选取的），写线程就会扫描读线程的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针，看看旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">列表中有哪些是与这些</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针匹配的。如果某个旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">不与任何读线程的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针匹配，那么销毁该</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">就是安全的。否则写线程就会仍将其保留在链表中，直到下次扫描。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">下面就是我们将要使用的数据结构。主要的共享结构是一个关于</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针（</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">HPRecType</span><span style="font-size: 12pt; font-family: 宋体;">）的单链表，由</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pHead_</span><span style="font-size: 12pt; font-family: 宋体;">所指向。该链表中的每一项都包含一个</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针（</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pHazard_</span><span style="font-size: 12pt; font-family: 宋体;">）、一个标志着该</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针是否正处于使用中的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">flag</span><span style="font-size: 12pt; font-family: 宋体;">（</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">active_</span><span style="font-size: 12pt; font-family: 宋体;">）、以及一个指向下一节点的指针（</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pNext_</span><span style="font-size: 12pt; font-family: 宋体;">）。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">HPRecType</span><span style="font-size: 12pt; font-family: 宋体;">类提供了两个原语：</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Acquire</span><span style="font-size: 12pt; font-family: 宋体;">和</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Release</span><span style="font-size: 12pt; font-family: 宋体;">。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">HPRecType::Acquire</span><span style="font-size: 12pt; font-family: 宋体;">返回一个指向一个</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">HPRecType</span><span style="font-size: 12pt; font-family: 宋体;">节点的指针，姑且称其为</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">p</span><span style="font-size: 12pt; font-family: 宋体;">。如此一来，获取了该指针的线程就可以设置</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">p-&gt;pHazard_</span><span style="font-size: 12pt; font-family: 宋体;">，并以此确保其它线程会小心对待该指针。而当该线程不再使用该指针时，它就会调用</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">HPRecType::Release(p)</span><span style="font-size: 12pt; font-family: 宋体;">。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">// Hazard pointer record<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">class HPRecType {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>HPRecType * pNext_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>int active_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// Global header of the HP list<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>static HPRecType * pHead_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// The length of the list<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>static int listLen_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">public:<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// Can be used by the thread<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;</span><span style="">&nbsp; </span>// that acquired it<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>void * pHazard_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>static HPRecType * Head() {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return pHead_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// Acquires one hazard pointer<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>static HPRecType * Acquire() {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// Try to reuse a retired HP record<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>HPRecType * p = pHead_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>for (; p; p = p-&gt;pNext_) {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if (p-&gt;active_ ||<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>!CAS(&amp;p-&gt;active_, 0, 1))<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>continue;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// Got one!<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return p;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// Increment the list length<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int oldLen;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>do {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>oldLen = listLen_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>} while (!CAS(&amp;listLen_, oldLen,<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>oldLen + 1));<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// Allocate a new one<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>HPRecType * p = new HPRecType;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>p-&gt;active_ = 1;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>p-&gt;pHazard_ = 0;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// Push it to the front<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>do {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>old = pHead_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>p-&gt;pNext_ = old;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>} while (!CAS(&amp;pHead_, old, p));<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return p;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// Releases a hazard pointer<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>static void Release(HPRecType* p) {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>p-&gt;pHazard_ = 0;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>p-&gt;active_ = 0;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">};<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">// Per-thread private variable<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">__per_thread__ vector&lt;Map&lt;K, V&gt; *&gt; rlist;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">每个线程都拥有一个“退役链表”（在我们的实现中，这实际上就是一个</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">vector&lt;Map&lt;K,V&gt;*&gt;</span><span style="font-size: 12pt; font-family: 宋体;">），“退役链表”是一个容器，负责存放该线程不再需要的、一旦其它线程也不再使用它们就可以被</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete</span><span style="font-size: 12pt; font-family: 宋体;">掉的指针。“退役链表”的访问不需要被同步，因为它位于每线程存储区中；永远都只有一个线程（即拥有它的线程）能访问它。我们使用</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">__per_thread__</span><span style="font-size: 12pt; font-family: 宋体;">修饰符来省却分配线程相关存储的麻烦。通过这一办法，每当一个线程想要销毁旧的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">时，它就只需要调用</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Retire</span><span style="font-size: 12pt; font-family: 宋体;">函数即可。（注意，正如上期文章中提到的，为了简单起见，我们并未在代码中插入内存屏障。）</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">template &lt;class K, class V&gt;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">class WRRMMap {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>Map&lt;K, V&gt; * pMap_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>...<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">private:<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>static void Retire(Map&lt;K, V&gt; * pOld) {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// put it in the retired list<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>rlist.push_back(pOld);<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>if (rlist.size() &gt;= R) {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Scan(HPRecType::Head());<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">};<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">毫无隐瞒，就这些！现在，我们的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Scan</span><span style="font-size: 12pt; font-family: 宋体;">函数会进行一个差集计算的算法，即当前线程的“退役链表”作为一个集合，其它所有线程的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针作为一个集合，求它们两个的差集。那么，这个差集的意义又是什么呢？让我们停下来考虑一下：前一个集合是当前线程所认为没有用的旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">指针，而后一集合则代表那些正被某个或某几个线程使用着的旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">指针（即那些线程的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针所指的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">）但是后者相当于“濒死”指针！根据“退役链表”和</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针的定义，如果一个指针“退役”了并且没有被任何一个线程的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针所指（使用）的话，该指针所指的旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">就是可以被安全地销毁的。换言之，第一个集合与第二个集合的差集正是那些可被安全销毁的旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">的指针。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><strong><span style="font-size: 13.5pt; font-family: 宋体;">主算法</span></strong><strong><span style="font-size: 13.5pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></strong></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">OK</span><span style="font-size: 12pt; font-family: 宋体;">，现在就让我们来看看如何实现</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Scan</span><span style="font-size: 12pt; font-family: 宋体;">算法，以及它能够提供什么样的保证。在进行</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Scan</span><span style="font-size: 12pt; font-family: 宋体;">时，我们需要对</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">rlist</span><span style="font-size: 12pt; font-family: 宋体;">（退役链表）跟</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pHead</span><span style="font-size: 12pt; font-family: 宋体;">（</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针链表）所代表的两个集合作差集运算。换言之：“对于退役链表中的每个指针，检查它是否同样位于</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针链表（</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pHead</span><span style="font-size: 12pt; font-family: 宋体;">）中，如果不是，则它属于差集，即可被安全销毁。”此外，为了对该算法加以优化，我们可以在进行查找之前先对</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针链表进行排序，然后对每个退役指针在其中进行二分查找。下面就是</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Scan</span><span style="font-size: 12pt; font-family: 宋体;">的实现：</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">void Scan(HPRecType * head) {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// Stage 1: Scan hazard pointers list<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// collecting all non-null ptrs<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>vector&lt;void*&gt; hp;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>while (head) {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void * p = head-&gt;pHazard_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if (p) hp.push_back(p);<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>head = head-&gt;pNext_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// Stage 2: sort the hazard pointers<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>sort(hp.begin(), hp.end(),<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>less&lt;void*&gt;());<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// Stage 3: Search for'em!<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>vector&lt;Map&lt;K, V&gt;*&gt;::iterator i =<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>rlist.begin();<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>while (i != rlist.end()) {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if (!binary_search(hp.begin(),<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>hp.end(),<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>*i) {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// Aha!<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>delete *i;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if (&amp;*i != &amp;rlist.back()) {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>*i = rlist.back();<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>rlist.pop_back();<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>} else {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>++i;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">最后一个循环做了实际的工作。其中运用了一点小小的技巧来避免</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">rlist vector</span><span style="font-size: 12pt; font-family: 宋体;">的重排：在</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete</span><span style="font-size: 12pt; font-family: 宋体;">了</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">rlist</span><span style="font-size: 12pt; font-family: 宋体;">中的一个可被删除的指针之后，我们将那个指针的位置用</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">rlist</span><span style="font-size: 12pt; font-family: 宋体;">的最后一个元素来填充，然后再将实际的最后一个元素</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pop</span><span style="font-size: 12pt; font-family: 宋体;">掉。这样做是允许的，因为</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">rlist</span><span style="font-size: 12pt; font-family: 宋体;">中的元素并不要求有序。此外我们使用了</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">C++</span><span style="font-size: 12pt; font-family: 宋体;">标准库函数</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">sort</span><span style="font-size: 12pt; font-family: 宋体;">跟</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">binary_search</span><span style="font-size: 12pt; font-family: 宋体;">。不过你可以将</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">vector</span><span style="font-size: 12pt; font-family: 宋体;">换成你所喜欢的容器，如哈希表这类易于搜索的数据结构。一个均衡的哈希表的期望查找时间是常数，而且建造这样一个哈希表也很容易，因为它是完全私有的，而且在组织它之前你知道其中所有的值。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">那么，</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Scan</span><span style="font-size: 12pt; font-family: 宋体;">的效率又如何呢？首先，注意到这整个算法是等待无关的（正如上文所介绍的那样）：每个线程的执行时间都不依赖于其它任何线程的行为。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">其次，</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">rlist</span><span style="font-size: 12pt; font-family: 宋体;">的平均大小是一个任意值，记为</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">R</span><span style="font-size: 12pt; font-family: 宋体;">，我们将该值用作触发</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Scan</span><span style="font-size: 12pt; font-family: 宋体;">的</span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">阈值（见上文代码中的</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">WRRMMap&lt;K,V&gt;::Replace</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">函数）。如果我们将</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">指针存放在一个哈希表中（而不是原先用的有序</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">vector</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">），那么</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Scan</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">算法的期望复杂度就可以降为</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">O(R)</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">。最后，已退役但尚存活在系统中（未被</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">）的旧</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">的最大数目是</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">N*R</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">，其中</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">N</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">为写线程的数目（译注：因为只有写线程才会将旧</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">替换下来，而每个写线程都有一个私有的</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">rlist</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">退役链表，其最大长度为</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">R</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">）。至于</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">R</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">的具体值，一个良好的选择是</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">(1+K)H</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">，其中</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">H</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">为</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">指针的数目（即我们的代码中的</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">listLen</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">，在我们的例子中这也就是读线程的数目（因为每个读线程有且仅有一个</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Hazard</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">指针）），而</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">K</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">则是某个小的正常数，如</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">1/4</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">。因此</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">R</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">大于</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">H</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">且与</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">H</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">成正比。这样一来，每次</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Scan</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">都能保证</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">掉</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">R-H</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">（即</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">O(R)</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">）个节点（旧</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">），而如果我们使用的是哈希表的话，时间复杂度就是</span></span><span class="style1"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">O(R)</span></span><span class="style1"><span style="font-size: 12pt; font-family: 宋体;">。因此，确定一个节点是否可被安全销毁的分摊时间复杂度为常数。</span></span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><strong><span style="font-size: 13.5pt; font-family: 宋体;">缝合</span></strong><strong><span style="font-size: 13.5pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">WRRMMap<o:p></o:p></span></strong></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">下面我们就来把</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针缝合进</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">WRRMMap</span><span style="font-size: 12pt; font-family: 宋体;">的原语，即</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Lookup</span><span style="font-size: 12pt; font-family: 宋体;">和</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Update</span><span style="font-size: 12pt; font-family: 宋体;">中。对于写线程（执行</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Update</span><span style="font-size: 12pt; font-family: 宋体;">的线程）来说，它们所需要做的就是在正常情况下应当</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">delete pMap_</span><span style="font-size: 12pt; font-family: 宋体;">的地方改成调用</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">WRRMMap&lt;K,V&gt;::Retire</span><span style="font-size: 12pt; font-family: 宋体;">。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">void Update(const K&amp;k, const V&amp;v){<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>Map&lt;K, V&gt; * pNew = 0;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>do {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Map&lt;K, V&gt; * pOld = pMap_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>delete pNew;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pNew = new Map&lt;K, V&gt;(*pOld);<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>(*pNew)[k] = v;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>} while (!CAS(&amp;pMap_, pOld, pNew));<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>Retire(pOld);<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">读线程首先得通过</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">HPRecType::Acquire</span><span style="font-size: 12pt; font-family: 宋体;">获得一个</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针，并将其赋为</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">，以便写线程可以查找到。当读线程使用完毕该指针之后，就通过</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">HPRecType::Release</span><span style="font-size: 12pt; font-family: 宋体;">来释放对应的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">V Lookup(const K&amp;k){<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>HPRecType * pRec = HPRecType::Acquire();<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>Map&lt;K, V&gt; * ptr;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>do {<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ptr = pMap_;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pRec-&gt;pHazard_ = ptr;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>} while (pMap_ != ptr);<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// Save Willy<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>V result = (*ptr)[k];<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// pRec can be released now<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>// because it's not used anymore<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>HPRecType::Release(pRec);<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><span style="">&nbsp;&nbsp; </span>return result;<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">}<o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">在上面的代码中，为什么读线程需要重新检查</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">是否等于</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">ptr</span><span style="font-size: 12pt; font-family: 宋体;">呢（</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">while(pMap_!=ptr)</span><span style="font-size: 12pt; font-family: 宋体;">）？考虑下面这种情况：</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">原先指向</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map m</span><span style="font-size: 12pt; font-family: 宋体;">，读线程将</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">ptr</span><span style="font-size: 12pt; font-family: 宋体;">赋为</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">，也就是</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">&amp;m</span><span style="font-size: 12pt; font-family: 宋体;">，接着读线程就进入了休眠（此时它还未来得及将</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针指向</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">m</span><span style="font-size: 12pt; font-family: 宋体;">）。而就在它的休眠期间，一个写线程偷偷溜进来，更新了</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">，然后将</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">原先所指的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">（即</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">m</span><span style="font-size: 12pt; font-family: 宋体;">）放到了退役链表中，并紧接着检查各线程的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针，由于刚才的读线程在进入休眠时还未来得及设置</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针，故</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">m</span><span style="font-size: 12pt; font-family: 宋体;">被认为是可以销毁的旧</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">，于是被写线程销毁。而这时读线程醒来了，并接着未完成的工作，将</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针赋为</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">ptr(</span><span style="font-size: 12pt; font-family: 宋体;">即</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">&amp;m)</span><span style="font-size: 12pt; font-family: 宋体;">。如果是这样的话，一旦读线程接下来对</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">ptr</span><span style="font-size: 12pt; font-family: 宋体;">进行解引用，它就会读取到被破坏的数据或访问了未被映射的内存。以上就是读线程需要再次检查</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">的原因。如果结果发现</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">并不等于</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">&amp;m</span><span style="font-size: 12pt; font-family: 宋体;">的话，读线程就无法确定将</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">m</span><span style="font-size: 12pt; font-family: 宋体;">退役掉的写线程是否看到了它的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针被置为</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">&amp;m</span><span style="font-size: 12pt; font-family: 宋体;">（换句话说，是否已将</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">m</span><span style="font-size: 12pt; font-family: 宋体;">销毁）。所以此时读线程继续对</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">m</span><span style="font-size: 12pt; font-family: 宋体;">进行读取就是不安全的，所以读线程需要从头来过。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">如果读线程而后发现</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">的确指向</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">m</span><span style="font-size: 12pt; font-family: 宋体;">，那么此时从</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">m</span><span style="font-size: 12pt; font-family: 宋体;">读取就是安全的。那么，这是否意味着在两次读取</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">之间，</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">没有被改动过呢？不一定，在这期间</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">m</span><span style="font-size: 12pt; font-family: 宋体;">可能被移除并再次挂到</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">上（一次或多次），但这并不要紧。要紧的是在第二次读取的时候</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">m</span><span style="font-size: 12pt; font-family: 宋体;">挂在</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">pMap_</span><span style="font-size: 12pt; font-family: 宋体;">上，而此时读线程的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针也已经指向它了。因此从这个点以后（知道</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针被改变），访问</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">m</span><span style="font-size: 12pt; font-family: 宋体;">都是安全的。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">这样</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Lookup</span><span style="font-size: 12pt; font-family: 宋体;">和</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Update</span><span style="font-size: 12pt; font-family: 宋体;">就都成了锁无关的（不过，仍不是等待无关的）：读线程不会阻塞写线程，多个读线程之间也不会互相撞车（这一点是引用计数方案所做不到的）。所以，我们得到了一个完美的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">WRRM Map</span><span style="font-size: 12pt; font-family: 宋体;">：读取操作非常快，而且不会互相冲突，更新操作仍然很快，而且能够保证整体进展。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">如果我们想要</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Lookup</span><span style="font-size: 12pt; font-family: 宋体;">成为等待无关的，我们可以使用“陷阱”（</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">trap</span><span style="font-size: 12pt; font-family: 宋体;">）技术</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">[4]</span><span style="font-size: 12pt; font-family: 宋体;">，它是基于</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针的一门技术。在前面的代码中，当读线程将其</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针赋为</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">ptr</span><span style="font-size: 12pt; font-family: 宋体;">时，它其实是想捕获某个特定的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">（</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">*ptr</span><span style="font-size: 12pt; font-family: 宋体;">）。而使用陷阱技术，读线程就可以设置一个“陷阱”，该“陷阱”保证能够捕获住某个有效的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">，这样</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Lookup</span><span style="font-size: 12pt; font-family: 宋体;">就成了等待无关的算法，同样，如果我们有自动垃圾收集机制的话，</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Lookup</span><span style="font-size: 12pt; font-family: 宋体;">也是等待无关的（参考专栏上一期的文章）。要想了解“陷阱”技术的细节，可参卡</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">[4]</span><span style="font-size: 12pt; font-family: 宋体;">。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><strong><span style="font-size: 13.5pt; font-family: 宋体;">泛化</span></strong><strong><span style="font-size: 13.5pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></strong></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">到这里为止，我们几乎已经完成了一个坚实的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">设计。不过还有一些事情需要考虑，以便让你对该技术形成一个全面的认识。完整的参考见</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">[3]</span><span style="font-size: 12pt; font-family: 宋体;">。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">我们已经知道如何才能共享一个</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">map</span><span style="font-size: 12pt; font-family: 宋体;">，然而倘若我们想要共享的是许多个对象呢？答案很简单：首先，我们的算法可以自然的推广到每线程多个</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针的情况。不过，通常在任何特定的时刻，一个线程需要同时保护的指针数目是非常有限的。此外，</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针可以被“重载”（因为它们的类型是</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">void*</span><span style="font-size: 12pt; font-family: 宋体;">）：一个线程可以将同一个</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针用在任何数目的数据结构身上，只要满足同一时刻</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针只被用于其中某一个数据结构这一条件即可。大多数时候，每线程拥有一个或两个</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针对于整个程序来说就已经足够了。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">最后注意一下，</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Lookup</span><span style="font-size: 12pt; font-family: 宋体;">在完成了一次</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">lookup</span><span style="font-size: 12pt; font-family: 宋体;">之后立即调用了</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">HPRecType::Release</span><span style="font-size: 12pt; font-family: 宋体;">。在一个考虑性能因素的应用程序中，我们可以只获取</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针一次，并用它来进行多次</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">lookup</span><span style="font-size: 12pt; font-family: 宋体;">，最后才释放它（一次）。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><strong><span style="font-size: 13.5pt; font-family: 宋体;">结论</span></strong><strong><span style="font-size: 13.5pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></strong></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">长
久以来人们一直试图解决锁无关算法的内存释放问题，有时人们甚至觉得对于这个问题并不存在令人满意的答案。然而，实际上我们只需作一点小小的辅助性工作，
同时在线程私有跟线程共享数据之间小心操作，我们就能够创造出一种在速度和内存占用方面皆给出了强大且令人满意的保证的算法。此外，尽管我们通篇使用了</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">WRRMMap</span><span style="font-size: 12pt; font-family: 宋体;">作为例子，但</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针技术实际上能够被运用到复杂得多的数据结构上。对于那些大小可以任意扩充和缩小的动态数据结构来说内存回收问题则更显重要；例如，设想一个拥有上千个链表、其中每个链表的大小都可能增长为上百万个节点的程序，这种场合正是</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针展现其强大身手的地方。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: 宋体;">一个读线程所能干的最糟糕的事情就是在未能将它的</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针置为空的情况下就中止了，这会导致它的每个</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">hazard</span><span style="font-size: 12pt; font-family: 宋体;">指针所指向的对象永远也不能被释放。</span><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><em><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">Bullet</span></em><em><span style="font-size: 12pt; font-family: 宋体;">探长冲进了他的债务人的办公室，并立即认识到他今天是不可能拿回他的钱了。于是他立即说道：“伙计，你已经上了我的黑名单了。日后我会再来拜访你，除非你死了。而且就算你‘不幸’故去，你欠的债也将不会超过</span></em><em><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">100</span></em><em><span style="font-size: 12pt; font-family: 宋体;">块。再见！”</span></em><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><strong><span style="font-size: 13.5pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">References<o:p></o:p></span></strong></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">[1] Alexandrescu, Andrei. "Generic&lt;Programming&gt;: Lock-Free Data Structures," <em>C++ Users Journal</em>, October 2004. <o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">[2]
Doherty, Simon, David L. Detlefs, Lindsay Grove, Christine H. Flood,
Victor Luchangco, Paul A. Martin, Mark Moir, Nir Shavit, and Jr. Guy L.
Steele. "DCAS is not a silver bullet for nonblocking algorithm design."
<em>Proceedings of the Sixteenth Annual ACM Symposium on Parallelism in Algorithms and Architectures</em>, pages 216-224. ACM Press, 2004. ISBN 1-58113-840-7. <o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">[3] Michael, Maged M. "Hazard Pointers: Safe Memory Reclamation for Lock-Free Objects," <em>IEEE Transactions on Parallel and Distributed Systems</em>, pages 491-504. IEEE, 2004. <o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">[4] Michael, Maged M. "Practical Lock-Free and Wait-Free LL/SC/VL Implementations Using 64-Bit CAS," <em>Proceedings of the 18th International Conference on Distributed Computing</em>, LNCS volume 3274, pages 144-158, October 2004. <o:p></o:p></span></p>

<p style="text-align: left;" class="MsoNormal" align="left"><span style="font-size: 12pt; font-family: &quot;Lucida Console&quot;;" lang="EN-US">[5]
Tang, Hong, Kai Shen, and Tao Yang. "Program Transformation and Runtime
Support for Threaded MPI Execution on Shared-memory Machines," <em>ACM Transactions on Programming Languages and Systems</em>, 22(4):673-700, 2000 (<a href="http://citeseer.ist.psu.edu/tang99program.html">citeseer.ist.psu.edu/tang99program.html</a>). <o:p></o:p></span></p>

<p class="MsoNormal"><span style="font-family: &quot;Lucida Console&quot;;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>
]]></description>
</item><item>
<title><![CDATA[动态配置log4j]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=newlulu&amp;id=11235</link>
<author>newlulu</author>
<pubDate>2006/1/16 22:40:07</pubDate>
<description><![CDATA[1&nbsp;配置外部配置文件来配置的基本步骤<BR>1.1　一个运用配置文件的实例<BR>Log4j之所以能成功的原因之一是它的灵活性。但如果只是简单的调用BasicConfigurator.configure()来进行配置工作，那么所有的配置都是在函数中写死的，以后修改配置就要修改原代码，这就不能体现出log4j的灵活性了，所以基本上不会通过BasicConfigurator.configure()来进行配置工作的。<BR>为了增加软件的灵活性，最常用的做法就是使用配置文件，如web.xml之于J2EE，struts-config.xml之于struts一样，log4j也提供了让我们把配置信息从程序转移到配置文件中的方法。Log4j提供了两种方式的配置文件：XML文件和Java的property配置文件。通过把配置信息转移到外部文件中，当我们要修改配置信息时，就可以直接修改配置文件而不用去修改代码了，下面，我们就来完成一个通过配置文件来实现log4j的实例。<BR>例2-a：<BR>package&nbsp;TestLog4j;<BR>import&nbsp;org.apache.log4j.Logger;<BR>import&nbsp;org.apache.log4j.BasicConfigurator;<BR>import&nbsp;org.apache.log4j.PropertyConfigurator;<BR>import&nbsp;org.apache.log4j.Priority;&nbsp;public&nbsp;class&nbsp;TestLog4j&nbsp;<BR>{<BR>static&nbsp;Logger&nbsp;logger&nbsp;=&nbsp;Logger.getLogger(TestLog4j.class.getName());<BR>public&nbsp;TestLog4j(){}<BR><BR>public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)<BR>{<BR>//通过BasicConfigurator类来初始化<BR>//BasicConfigurator.configure();<BR>//（1）通过配置文件来初始化<BR>PropertyConfigurator.configure("F:\\nepalon\\log4j.properties");<BR><BR>logger.debug("Start&nbsp;of&nbsp;the&nbsp;main()&nbsp;in&nbsp;TestLog4j");　//代码（2）<BR>logger.info("Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;INFO");<BR>logger.warn("Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;WARN");<BR>logger.error("Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;ERROR");<BR>logger.fatal("Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;FATAL");<BR>logger.log(Priority.WARN,&nbsp;"Testing&nbsp;a&nbsp;log&nbsp;message&nbsp;use&nbsp;a&nbsp;alternate&nbsp;form");<BR>logger.debug(TestLog4j.class.getName());　//代码（2）<BR>}<BR>}<BR>在这个例子中，我们用PropertyConfigurator.configure("F:\\nepalon\\log4j.properties")代替BasicConfigurator.configure()进行配置。PropertyConfigurator.configure()函数的参数可以是一个properties文件所在路径的String对象，可以是一个properties文件所在路径的URL对象，也可以是一个properties对象。通过PropertyConfigurator.configure()可以通过指定的properties文件来配置信息。如果要用XML文件进行信息配置，可以在代码中调用DOMConfigurator()函数来进行配置工作。在这里，我们只以properties文件来完成例子。接着，我们来看一下log4j.properties文件中都有些什么东西：<BR>例2-b：<BR>log4j.rootLogger&nbsp;=&nbsp;DEBUG,&nbsp;A1<BR>log4j.appender.A1&nbsp;=&nbsp;org.apache.log4j.ConsoleAppender<BR>log4j.appender.A1.layout&nbsp;=&nbsp;org.apache.log4j.PatternLayout<BR>log4j.appender.A1.layout.ConversionPattern&nbsp;=&nbsp;%-4r&nbsp;[%t]&nbsp;%-5p&nbsp;%c&nbsp;%x&nbsp;-&nbsp;%m%n<BR>运行这个实例，运行结果为<BR>0&nbsp;[main]&nbsp;DEBUG&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Start&nbsp;of&nbsp;the&nbsp;main()&nbsp;in&nbsp;TestLog4j<BR>20&nbsp;[main]&nbsp;INFO&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;INFO<BR>20&nbsp;[main]&nbsp;WARN&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;WARN<BR>20&nbsp;[main]&nbsp;ERROR&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;ERROR<BR>20&nbsp;[main]&nbsp;FATAL&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;FATAL<BR>180&nbsp;[main]&nbsp;WARN&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Testing&nbsp;a&nbsp;log&nbsp;message&nbsp;use&nbsp;a&nbsp;alternate&nbsp;form<BR>180&nbsp;[main]&nbsp;DEBUG&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;TestLog4j.TestLog4j<BR>下面，我们分析一下这个配置文件。<BR>1)&nbsp;由于每一个Logger对旬都有一个级别，文件的第一行就是定义了一个Logger及其级别。在这里定义了一个根记录器（root&nbsp;logger），这涉及到记录器的层次问题，在些暂时不深入讨论，在后面的章节再进行讨论。<BR>2)&nbsp;第二行定义了一个名为A1的输出流，这个流就是控制台，所以通过Logger对象打印的信息会在控制台输出。<BR>3)&nbsp;第三行定义了打印信息的布局。在这里我们用PatternLayout作为此记录器的布局，PatternLayout允许你以灵活的格式来打印信息。<BR>4)&nbsp;第四行指定的打印信息的具体格式，从结果可知，这个实例的打印格式为：当前打印语句所使用的时间&nbsp;[日志所在的线程]&nbsp;打印的级别&nbsp;当前日志所在的类的全名&nbsp;日志信息。<BR>现在我们来修改一下这个记录器的级别，把第一行的DEBUG改为INFO，再运行程序，结果将变为：<BR>0&nbsp;[main]&nbsp;INFO&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;INFO<BR>10&nbsp;[main]&nbsp;WARN&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;WARN<BR>10&nbsp;[main]&nbsp;ERROR&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;ERROR<BR>10&nbsp;[main]&nbsp;FATAL&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;FATAL<BR>10&nbsp;[main]&nbsp;WARN&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Testing&nbsp;a&nbsp;log&nbsp;message&nbsp;use&nbsp;a&nbsp;alternate&nbsp;form<BR>由于这个Logger的级别变为INFO，而代码（2）是调用debug()函数来输出日志信息时只能当记录器级别为DEBUG时才输出信息，所以代码（2）将不输出信息。<BR>1.2　实例原理<BR>1.2.1　初始化配置信息<BR>如果要通过JAVA的properties文件来配置信息，那么在代码中就要通过PropertyConfigurator.configure()函数从properties文件中加载配置信息，这个函数有三种参数形式：一个properties文件所在路径的String对象，可以是一个properties文件所在路径的URL对象，也可以是一个properties对象。如果要用XML文件来配置信息，则可用类型的<BR>DOMConfigurator()函数来从一个XML文件中加载配置信息。<BR>1.2.2　输出端Appender<BR>在上面的例子中，我们都是简单的把日志信息输出到控制台中。其实在log4j中还可以把日志信息输出到其它的输出端，对于同一个日志信息，我们还可以让它同时输出到多个输出端中，如同时在控制台和文件中进行打印。一个输出端就是一个appender。要在配置文件中定义一个appender有三步：<BR>1)&nbsp;在定义一个记录器的同时定义出该记录器的输出端appender。在例2的配置文件的第一句log4j.rootLogger&nbsp;=&nbsp;DEBUG,&nbsp;A1中，我们定义了一个根记录器，它的级别为DEBUG，它有一个appender名为A1。定义根记录器的格式为log4j.rootLogger&nbsp;=&nbsp;[&nbsp;level&nbsp;],&nbsp;appendName1,&nbsp;appendName2,&nbsp;…appendNameN。同一个记录器可有多个输出端。<BR>2)&nbsp;定义appender的输出目的地。定义一个appender的输出目的地的格式为log4j.appender.appenderName&nbsp;=&nbsp;fully.qualified.name.of.appender.class。log4j提供了以下几种常用的输出目的地：<BR>?&nbsp;org.apache.log4j.ConsoleAppender，将日志信息输出到控制台<BR>?&nbsp;org.apache.log4j.FileAppender，将日志信息输出到一个文件<BR>?&nbsp;org.apache.log4j.DailyRollingFileAppender，将日志信息输出到一个，并且每天输出到一个新的日志文件<BR>?&nbsp;org.apache.log4j.RollingFileAppender，将日志信息输出到一个文件，通过指定文件的的尺寸，当文件大小到达指定尺寸的时候会自动把文件改名，如名为example.log的文件会改名为example.log.1，同时产生一个新的example.log文件。如果新的文件再次达到指定尺寸，又会自动把文件改名为example.log.2，同时产生一个example.log文件。依此类推，直到example.log.&nbsp;MaxBackupIndex，MaxBackupIndex的值可在配置文件中定义。<BR>?&nbsp;org.apache.log4j.WriterAppender，将日志信息以流格式发送到任意指定的地方。<BR>?&nbsp;org.apache.log4j.jdbc.JDBCAppender，通过JDBC把日志信息输出到数据库中。<BR>在例2中，log4j.appender.A1&nbsp;=&nbsp;org.apache.log4j.ConsoleAppender定义了名为A1的appender的输出目的地为控制台，所以日志信息将输出到控制台。<BR>3)&nbsp;定义与所选的输出目的地相关的参数，定义格式为：<BR>log4j.appender.appenderName.optionName1&nbsp;=&nbsp;value1<BR>……<BR>log4j.appender.appenderName.optionNameN&nbsp;=&nbsp;valueN<BR>其中一个最常用的参数layout将在下面介绍。<BR>1.2.3　输出格式（布局）layout<BR>通过appender可以控制输出的目的地，而如果要控制输出的格式，就可通过log4j的layout组件来实现。通过配置文件定义一个appender的输出格式，也通常需要两个步骤：<BR>1)&nbsp;定义appender的布局模式。定义一个appender的布局模式的格式为log4j.appender.appenderName.layout&nbsp;=&nbsp;fully.qualified.name.of.layout.class。Log4j提供的布局模式有以下几种：<BR>?&nbsp;org.apache.log4j.HTMLLayout，以HTML表格形式布局<BR>?&nbsp;org.apache.log4j.PatternLayout，可以灵活地指定布局模式<BR>?&nbsp;org.apache.log4j.SimpleLayout，包含日志信息的级别和信息字符串<BR>在例2　中log4j.appender.A1.layout&nbsp;=&nbsp;org.apache.log4j.PatternLayout定义了名为A1的appender的布局模式为PatternLayout。<BR>2)&nbsp;定义与所选的布局模式相关的设置信息，定义格式为：<BR>log4j.appender.appenderName.layout.optionName1&nbsp;=&nbsp;value1<BR>……<BR>log4j.appender.appenderName.layout.optionNameN&nbsp;=&nbsp;valueN<BR>选择了不同的布局模式可能会有不同的设置信息。实例2所选的布局模式PatternLayout的一个PatternLayout为ConversionPattern&nbsp;，通过定义这个PatternLayout的值，我们可以指定输出信息的输出格式。在例2的配置文件中的定义如下log4j.appender.A1.layout.ConversionPattern&nbsp;=&nbsp;%-4r&nbsp;[%t]&nbsp;%-5p&nbsp;%c&nbsp;%x&nbsp;-&nbsp;%m%n。在下面，我们将介绍布局模式PatternLayout的参数ConversionPattern的各个值代表的含义。<BR>1.2.4　ConversionPattern参数的格式含义<BR>格式名&nbsp;含义<BR>%c&nbsp;输出日志信息所属的类的全名<BR>%d&nbsp;输出日志时间点的日期或时间，默认格式为ISO8601，也可以在其后指定格式，比如：%d{yyy-MM-dd&nbsp;HH:mm:ss&nbsp;}，输出类似：2002-10-18-&nbsp;22：10：28<BR>%f&nbsp;输出日志信息所属的类的类名<BR>%l&nbsp;输出日志事件的发生位置，即输出日志信息的语句处于它所在的类的第几行<BR>%m&nbsp;输出代码中指定的信息，如log(message)中的message<BR>%n&nbsp;输出一个回车换行符，Windows平台为“\r\n”，Unix平台为“\n”<BR>%p&nbsp;输出优先级，即DEBUG，INFO，WARN，ERROR，FATAL。如果是调用debug()输出的，则为DEBUG，依此类推<BR>%r&nbsp;输出自应用启动到输出该日志信息所耗费的毫秒数<BR>%t&nbsp;输出产生该日志事件的线程名<BR>1.3　定义多个输出目的地的实例<BR>从上面的实例原理中我们已经知道，同一个日志信息可以同时输出到多个输出目的地，在这个例子中，我们将实现一个把日志信息同时输出到控制器、一个文件中的实例和数据库中。这个实例的Java代码我们沿用例2中的代码，我们只需修改配置文件即可。这也体现了log4j的灵活性。<BR>例3-a：<BR>create&nbsp;table&nbsp;log4j(<BR>logID&nbsp;int&nbsp;primary&nbsp;key&nbsp;identity,<BR>message&nbsp;varchar(1024),<BR>priority&nbsp;varchar(10),<BR>milliseconds&nbsp;int,<BR>category&nbsp;varchar(256),<BR>thread&nbsp;varchar(100),<BR>NDC&nbsp;varchar(256),<BR>createDate&nbsp;datetime,<BR>location&nbsp;varchar(256),<BR>caller&nbsp;varchar(100),<BR>method&nbsp;varchar(100),<BR>filename&nbsp;varchar(100),<BR>line&nbsp;int<BR>)<BR>例3-b：<BR>#1&nbsp;定义了两个输出端<BR>log4j.rootLogger&nbsp;=&nbsp;INFO,&nbsp;A1,&nbsp;A2,A3<BR><BR>#2&nbsp;定义A1输出到控制器<BR>log4j.appender.A1&nbsp;=&nbsp;org.apache.log4j.ConsoleAppender<BR>#3&nbsp;定义A1的布局模式为PatternLayout<BR>log4j.appender.A1.layout&nbsp;=&nbsp;org.apache.log4j.PatternLayout<BR>#4&nbsp;定义A1的输出格式<BR>log4j.appender.A1.layout.ConversionPattern&nbsp;=&nbsp;%-4r&nbsp;[%t]&nbsp;%-5p&nbsp;%c&nbsp;-&nbsp;%m%n<BR><BR>#5&nbsp;定义A2输出到文件<BR>log4j.appender.A2&nbsp;=&nbsp;org.apache.log4j.RollingFileAppender<BR>#6&nbsp;定义A2要输出到哪一个文件<BR>log4j.appender.A2.File&nbsp;=&nbsp;F:\\nepalon\\classes\\example3.log<BR>#7&nbsp;定义A2的输出文件的最大长度<BR>log4j.appender.A2.MaxFileSize&nbsp;=&nbsp;1KB<BR>#8&nbsp;定义A2的备份文件数<BR>log4j.appender.A2.MaxBackupIndex&nbsp;=&nbsp;3<BR>#9&nbsp;定义A2的布局模式为PatternLayout<BR>log4j.appender.A2.layout&nbsp;=&nbsp;org.apache.log4j.PatternLayout<BR>#10&nbsp;定义A2的输出格式<BR>log4j.appender.A2.layout.ConversionPattern&nbsp;=&nbsp;%d{yyyy-MM-dd&nbsp;hh:mm:ss}:%p&nbsp;%t&nbsp;%c&nbsp;-&nbsp;%m%n<BR><BR>#11区&nbsp;定义A3输出到数据库<BR>log4j.appender.A3&nbsp;=&nbsp;org.apache.log4j.jdbc.JDBCAppender<BR>log4j.appender.A3.BufferSize&nbsp;=&nbsp;40<BR>log4j.appender.A3.Driver&nbsp;=&nbsp;com.microsoft.jdbc.sqlserver.SQLServerDriver<BR>log4j.appender.A3.URL&nbsp;=&nbsp;jdbc:microsoft:sqlserver://127.0.0.1:1433;DatabaseName=nepalon<BR>log4j.appender.A3.User&nbsp;=&nbsp;sa<BR>log4j.appender.A3.Password&nbsp;=&nbsp;<BR>log4j.appender.A3.layout&nbsp;=&nbsp;org.apache.log4j.PatternLayout<BR>log4j.appender.A3.layout.ConversionPattern&nbsp;=&nbsp;INSERT&nbsp;INTO&nbsp;log4j&nbsp;(createDate,&nbsp;thread,&nbsp;priority,&nbsp;category,&nbsp;message)&nbsp;values(getdate(),&nbsp;'%t',&nbsp;'%-5p',&nbsp;'%c',&nbsp;'%m')<BR>配置文件中的6、7、8行显示了输出端为RollingFileAppender的特有参数及其运用的方法。11区显示了输出端为JDBCAppender的特有参数及其运用方法。在这着重讲解一下6、7、8行的作用。6行指定日志信息输出到哪个文件，7行指定日志文件的最大长度，最后要详细介绍8行。第8行的参数是设置备份文件的个数的参数，在这里我们设置为3，表示最多有3个备份文件，具体作用为：<BR>1)&nbsp;当example3.log文件的大小超过K时，就把文件改名为example3.log.1，同时生成一个新的example3.log文件<BR>2)&nbsp;当example3.log文件的大小再次超过1K，又把文件改名为example3.log.1。但由于此时example3.log.1已存在，则先把example3.log.1更名为example3.log.2，再把example3.log文件改名为example3.log.1<BR>3)&nbsp;同理，当example3.log文件的大小再次超过1K，先把example3.log.2文件更名为example3.log.3，把example3.log.1文件更名为example3.log.2，再把example3.log文件改名为example3.log.1<BR>4)&nbsp;当example3.log文件的大小再次超过1K，先把example3.log.2文件更名为example3.log.3，旧的example3.log.3文件将被覆盖；把example3.log.1文件更名为example3.log.2，旧的example3.log.2文件被覆盖；最后把example3.log文件改名为example3.log.1并覆盖掉旧的example3.log.1文件。<BR>运行结果将分为两部分<BR>在控制器中：<BR>0&nbsp;[main]&nbsp;INFO&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;INFO<BR>11&nbsp;[main]&nbsp;WARN&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;WARN<BR>21&nbsp;[main]&nbsp;ERROR&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;ERROR&nbsp;21&nbsp;[main]&nbsp;FATAL&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;FATAL<BR>21&nbsp;[main]&nbsp;WARN&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Testing&nbsp;a&nbsp;log&nbsp;message&nbsp;use&nbsp;a&nbsp;alternate&nbsp;form<BR>在文件example3.log中：<BR>2003-12-18&nbsp;04:23:02:INFO&nbsp;main&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;INFO<BR>2003-12-18&nbsp;04:23:02:WARN&nbsp;main&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;WARN<BR>2003-12-18&nbsp;04:23:02:ERROR&nbsp;main&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;ERROR<BR>2003-12-18&nbsp;04:23:02:FATAL&nbsp;main&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;FATAL<BR>2003-12-18&nbsp;04:23:02:WARN&nbsp;main&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Testing&nbsp;a&nbsp;log&nbsp;message&nbsp;use&nbsp;a&nbsp;alternate&nbsp;form<BR>1.4　配置log4j的总结<BR>这个教程到这里，关于配置log4j的配置文件的基本原理已经讲完了，而且通过例3我们已经可以完成基本的日志工作了。现在，我们就做一个总结。配置一个配置文件的基本步骤如下：<BR>1)&nbsp;定义一个Logger。在定义Logger时指定该Logger的级别级其输出目的地。定义Logger的格式为<BR>log4j.rootLogger&nbsp;=&nbsp;[&nbsp;level&nbsp;],&nbsp;appendName1,&nbsp;appendName2,&nbsp;…appendNameN。<BR>2)&nbsp;定义appender的输出目的地。定义一个appender的输出目的地的格式为<BR>log4j.appender.appenderName&nbsp;=&nbsp;fully.qualified.name.of.appender.class。<BR>log4j提供的输出端有ConsoleAppender、FileAppender&nbsp;、DailyRollingFileAppender、RollingFileAppender和WriterAppender。<BR>3)&nbsp;定义appender的除布局模式外的其它相关参数，如例3中第6、7、8定义了A2的相关参数。定义格式为<BR>log4j.appender.appenderName.optionName1&nbsp;=&nbsp;value1<BR>……<BR>log4j.appender.appenderName.optionNameN&nbsp;=&nbsp;valueN<BR>如果除了布局模式外不需要定义别的参数，可跳过这一步（如例3中的A1）。<BR>4)&nbsp;定义appender的布局模式。定义一个appender的布局模式的格式为<BR>log4j.appender.appenderName.layout&nbsp;=&nbsp;fully.qualified.name.of.layout.class。<BR>布局模式其实也是步骤3）中的一个部分，只是布局模式参数是每一个appender必须定义的参数。Log4j提供的布局模式有HTMLLayout、PatternLayout和SimpleLayout。<BR>5)&nbsp;定义与所选的布局模式相关的设置信息，定义格式为<BR>og4j.appender.appenderName.layout.optionName1&nbsp;=&nbsp;value1<BR>……<BR>log4j.appender.appenderName.layout.optionNameN&nbsp;=&nbsp;valueN<BR>2　记录器的层次Logger&nbsp;hierarchy<BR>2.1　何为记录器的层次hierarchy<BR>首先，我们先看一下何为层次，以我们最熟悉的继承为例，下面是一张类图<BR><BR>在这个继承体系中，类B是类C的父类，类A是类C的祖先类，类D是类C的子类。这些类之间就构成一种层次关系。在这些具有层次关系的类中，子类都可继承它的父类的特征，如类B的对象能调用类A中的非private实例变量和函数；而类C由于继承自类B，所以类B的对象可以同时调用类A和类B中的非private实例变量和函数。<BR>在log4j中，处于不同层次中的Logger也具有象类这样的继承关系。<BR>2.2　记录器的层次<BR>如果一个应用中包含了上千个类，那么也几乎需要上千个Logger实例。如何对这上千个Logger实例进行方便地配置，就是一个很重要的问题。Log4J采用了一种树状的继承层次巧妙地解决了这个问题。在Log4J中Logger是具有层次关系的。它有一个共同的根，位于最上层，其它Logger遵循类似包的层次。下面我们将进行介绍。<BR>2.2.1　根记录器root&nbsp;logger<BR>就象一个Java中的Object类一样，log4j中的logger层次中有一个称之为根记录器的记录器，其它所有的记录器都继承自这个根记录器。根记录器有两个特征：<BR>1)&nbsp;根记录器总是存在。就像Java中的Object类一样，因为用log4j输出日志信息是通过记录器来实现的，所以只要你应用了log4j，根记录器就肯定存在的。<BR>2)&nbsp;根记录器没有名称，所以不能通过名称来取得根记录器。但在Logger类中提供了getRootLogger()的方法来取得根记录器。<BR>2.2.2　记录器的层次<BR>Logger遵循类似包的层次。如<BR>static&nbsp;Logger&nbsp;rootLog&nbsp;=&nbsp;Logger.getRootLogger();<BR>static&nbsp;Logger&nbsp;log1&nbsp;=&nbsp;Logger.getLogger("test4j");<BR>static&nbsp;Logger&nbsp;log2&nbsp;=&nbsp;Logger.getLogger("test4j.test4j2");<BR>static&nbsp;Logger&nbsp;log3&nbsp;=&nbsp;Logger.getLogger("test4j.test4j2.test4j2");<BR>那么rootLog是log2的祖先子记录器，log1是log2的父子记录器，log3是log2的子记录器。记录器象Java中的类继承一样，子记录器可以继承父记录器的设置信息，也可以可以覆写相应的信息。<BR>首先先看一下记录器层次中的继承有什么用处。假设程序中的每个包都具有一些基本的日志信息，而包中的不同包可能会有些额外的日志信息要输出，这种情况就可以象处理Java中的类一样，运用Logger中的层次关系来达到目的。假设有个名为A的包，我包下的所有类都要把日志信息输出到控制台中；A.B包除了输出到控制台外还要输出到文件中；A.C包除了输出到控制台中还要输出到HTML文档中。这样我们就可以通过定义一个父记录器A，它负责把日志信息输出到控制台中；定义一个A的子记录器A.B，它负责把日志信息输出到文件中；定义一个A的子记录器A.C，它负责把日志信息输出到HTML文档中。<BR>记录器遵循的是类似包的层次，这样做为我们带来了大大的方便。Logger类中的getLogger()方法可以取得Logger对象，这个方法有三种参数形式String、Class和URL，其实不论是用哪一种，最终都是通过记录器的名字来取得记录器对象的。如果要取得一个名为A.B的记录器对象，我们可以Logger.getLogger(“A.B”)。但从上面的例子中，我们都是通过Logger.getLogger(TestLog4j.class.getName())这种方法来取得记录器对象。这是为什么呢？现在我们假设A.B的包下有一个类BClass，那么我们调用BClass.class.getName()得到的是这个类的全名A.B.BClass。所以当调用Logger.getLogger(BClass.class.getName())时，最理想的情况是返回名为A.B.BClass的记录器对象。但是如果不存在名为A.B.BClass的记录器时它会怎样呢？其实通过Logger类的getLogger方法取得记录器时存在下面两种情况：<BR>1)&nbsp;如果存在与所要找的名字完全相同的记录器，则返回相应的记录器对象。<BR>当调用Logger.getLogger(BClass.class.getName())时，如果定义了名为A.B.BClass的记录器，它就返回该记录器的对象。<BR>2)&nbsp;但如果找不到，它会尝试返回在记录器层次中与所要找的记录器最接近的记录器对象。<BR>当调用Logger.getLogger(BClass.class.getName())时，如果没有定义了名为A.B.BClass的记录器，那会尝试返回名为A.B的记录器的对象；如果又没有定义名为A.B的记录器，它会尝试返回名为A的记录器的对象；如果也没定义名为A的记录器，它就会返回根记录器的对象，而根记录器是必须存在的，所以你总能得到一个记录器对象。<BR>好了，现在我们回到前面的问题，我们为什么总要通过Logger.getLogger(BClass.class.getName())这种以类全名作为参数来取得记录器对象呢？其实这是为了管理方便。因为我们在定义设计Logger时也遵循类似包的规则，使设计器的名称与程序中的类包对应。如上面的假设中我们的程序中有A包，A包下有B包和C包，B包下又有类BClass，那么我们就可使设计器的名为A、A.B、A.C、A.B.BClass，以此类推。那么当我们通过类命名来取得设计器对象时，总能取到与所要的设计器最接近的设计器对象。<BR>2.3　如何应用记录器的层次<BR>2.3.1　如果定义及获取不同层次的记录器<BR>任何一个记录器的使用都有两个步骤：<BR>1)&nbsp;在配置文件中定义相应的记录器。<BR>在配置文件中定义记录器的格式有两种<BR>?&nbsp;定义根记录器的格式为<BR>log4j.rootLogger&nbsp;=&nbsp;[&nbsp;level&nbsp;],&nbsp;appendName1,&nbsp;appendName2,&nbsp;…appendNameN<BR>?&nbsp;定义一个非根记录器的格式为<BR>log4j.logger.loggerName1&nbsp;=&nbsp;[&nbsp;level&nbsp;],&nbsp;appendName1,…appendNameN<BR>……<BR>log4j.logger.loggerNameM&nbsp;=&nbsp;[&nbsp;level&nbsp;],&nbsp;appendName1,&nbsp;…appendNameN<BR>我们可以定义任意个非根记录器。<BR>2)&nbsp;在代码中调用Logger类的取得记录器方法取得相应的记录器对象。<BR>要取得根记录器对象可通过Logger.getRootLogger()函数，要取得非根记录器可通过Logger.getLogger()函数。<BR>理论知道就讲到这里，纸上得来终觉浅，下面，我们来小小演练一下。<BR>例4-a：<BR>package&nbsp;TestLog4j;<BR>import&nbsp;org.apache.log4j.Logger;<BR>import&nbsp;org.apache.log4j.PropertyConfigurator;<BR>import&nbsp;org.apache.log4j.Priority;<BR>import&nbsp;TestLog4j.TestLog4j2.TestLog4j2;<BR><BR>public&nbsp;class&nbsp;TestLog4j&nbsp;<BR>{<BR>static&nbsp;Logger&nbsp;logger&nbsp;=&nbsp;Logger.getLogger(TestLog4j.class.getName());　//（2）<BR>public&nbsp;TestLog4j(){}<BR><BR>public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)<BR>{<BR>//同时输出到控制台和一个文件的实例并实现了Logger的继承<BR>PropertyConfigurator.configure("F:\\nepalon\\log4j2.properties");<BR><BR>logger.debug("Start&nbsp;of&nbsp;the&nbsp;main()&nbsp;in&nbsp;TestLog4j");<BR>logger.info("Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;INFO");<BR>logger.warn("Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;WARN");<BR>logger.error("Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;ERROR");<BR>logger.fatal("Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;FATAL");<BR>logger.log(Priority.WARN,&nbsp;"Testing&nbsp;a&nbsp;log&nbsp;message&nbsp;use&nbsp;a&nbsp;alternate&nbsp;form");<BR>logger.debug(TestLog4j.class.getName());<BR>TestLog4j2&nbsp;testLog4j2&nbsp;=&nbsp;new&nbsp;TestLog4j2();　//（1）<BR>testLog4j2.testLog();<BR>}<BR>}<BR>在类TestLog4j中我们调用了另一个类TestLog4j2，下面看一下类TestLog4j2的代码。<BR>例4-b：<BR>package&nbsp;TestLog4j.TestLog4j2;<BR>import&nbsp;org.apache.log4j.Logger;<BR>import&nbsp;org.apache.log4j.PropertyConfigurator;<BR>import&nbsp;org.apache.log4j.Priority;<BR><BR>public&nbsp;class&nbsp;TestLog4j2&nbsp;<BR>{<BR>static&nbsp;Logger&nbsp;logger&nbsp;=&nbsp;Logger.getLogger(TestLog4j2.class.getName());　//（1）<BR>public&nbsp;TestLog4j2(){}<BR><BR>public&nbsp;void&nbsp;testLog()<BR>{<BR>//同时输出到控制台和一个文件的实例<BR>PropertyConfigurator.configure("F:\\nepalon\\log4j2.properties");<BR>logger.debug("2Start&nbsp;of&nbsp;the&nbsp;main()");<BR>logger.info("2Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;INFO");<BR>logger.warn("2Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;WARN");<BR>logger.error("2Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;ERROR");<BR>logger.fatal("2Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;FATAL");<BR>logger.log(Priority.DEBUG,&nbsp;"Testing&nbsp;a&nbsp;log&nbsp;message&nbsp;use&nbsp;a&nbsp;alternate&nbsp;form");<BR>logger.debug("2End&nbsp;of&nbsp;the&nbsp;main()");<BR>}<BR>}<BR>最后我们来看一下配置文件。<BR>例4-c：<BR>log4j2.properties文件内容<BR>＃1区<BR>####&nbsp;Use&nbsp;two&nbsp;appenders,&nbsp;one&nbsp;to&nbsp;log&nbsp;to&nbsp;console,&nbsp;another&nbsp;to&nbsp;log&nbsp;to&nbsp;a&nbsp;file<BR>log4j.rootLogger&nbsp;=&nbsp;debug,&nbsp;stdout<BR><BR>#2区<BR>#Print&nbsp;only&nbsp;messages&nbsp;of&nbsp;priority&nbsp;WARN&nbsp;or&nbsp;higher&nbsp;for&nbsp;your&nbsp;category<BR>log4j.logger.TestLog4j=&nbsp;,&nbsp;R<BR>log4j.logger.TestLog4j.TestLog4j2=WARN<BR><BR>#3区<BR>####&nbsp;First&nbsp;appender&nbsp;writes&nbsp;to&nbsp;console<BR>log4j.appender.stdout=org.apache.log4j.ConsoleAppender<BR>log4j.appender.stdout.layout=org.apache.log4j.PatternLayout<BR><BR>#&nbsp;Pattern&nbsp;to&nbsp;output&nbsp;the&nbsp;caller's&nbsp;file&nbsp;name&nbsp;and&nbsp;line&nbsp;number.<BR>log4j.appender.stdout.layout.ConversionPattern=%5p&nbsp;[%t]&nbsp;(%F:%L)&nbsp;-&nbsp;%m%n<BR><BR>#4区<BR>####&nbsp;Second&nbsp;appender&nbsp;writes&nbsp;to&nbsp;a&nbsp;file<BR>log4j.appender.R=org.apache.log4j.RollingFileAppender<BR>log4j.appender.R.File=F:\\nepalon\\classes\\TestLog4j\\example.log<BR><BR>#&nbsp;Control&nbsp;the&nbsp;maximum&nbsp;log&nbsp;file&nbsp;size<BR>log4j.appender.R.MaxFileSize=100KB<BR>#&nbsp;Archive&nbsp;log&nbsp;files&nbsp;(one&nbsp;backup&nbsp;file&nbsp;here)<BR>log4j.appender.R.MaxBackupIndex=1<BR><BR>log4j.appender.R.layout=org.apache.log4j.PatternLayout<BR>log4j.appender.R.layout.ConversionPattern=%d{yyyy-MM-dd&nbsp;hh:mm:ss}:%p&nbsp;%t&nbsp;%c&nbsp;-&nbsp;%m%n<BR>先看一下运行结果。<BR>在控制台中的结果为：<BR>DEBUG&nbsp;[main]&nbsp;(?:?)&nbsp;-&nbsp;Start&nbsp;of&nbsp;the&nbsp;main()&nbsp;in&nbsp;TestLog4j<BR>INFO&nbsp;[main]&nbsp;(?:?)&nbsp;-&nbsp;Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;INFO<BR>WARN&nbsp;[main]&nbsp;(?:?)&nbsp;-&nbsp;Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;WARN<BR>ERROR&nbsp;[main]&nbsp;(?:?)&nbsp;-&nbsp;Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;ERROR<BR>FATAL&nbsp;[main]&nbsp;(?:?)&nbsp;-&nbsp;Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;FATAL<BR>WARN&nbsp;[main]&nbsp;(?:?)&nbsp;-&nbsp;Testing&nbsp;a&nbsp;log&nbsp;message&nbsp;use&nbsp;a&nbsp;alternate&nbsp;form<BR>DEBUG&nbsp;[main]&nbsp;(?:?)&nbsp;-&nbsp;TestLog4j.TestLog4j<BR>WARN&nbsp;[main]&nbsp;(?:?)&nbsp;-&nbsp;2Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;WARN<BR>ERROR&nbsp;[main]&nbsp;(?:?)&nbsp;-&nbsp;2Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;ERROR<BR>FATAL&nbsp;[main]&nbsp;(?:?)&nbsp;-&nbsp;2Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;FATAL<BR>输出文件的结果为：<BR>2003-12-19&nbsp;04:19:44:DEBUG&nbsp;main&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Start&nbsp;of&nbsp;the&nbsp;main()&nbsp;in&nbsp;TestLog4j<BR>2003-12-19&nbsp;04:19:44:INFO&nbsp;main&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;INFO<BR>2003-12-19&nbsp;04:19:44:WARN&nbsp;main&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;WARN<BR>2003-12-19&nbsp;04:19:44:ERROR&nbsp;main&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;ERROR<BR>2003-12-19&nbsp;04:19:44:FATAL&nbsp;main&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;FATAL<BR>2003-12-19&nbsp;04:19:44:WARN&nbsp;main&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;Testing&nbsp;a&nbsp;log&nbsp;message&nbsp;use&nbsp;a&nbsp;alternate&nbsp;form<BR>2003-12-19&nbsp;04:19:44:DEBUG&nbsp;main&nbsp;TestLog4j.TestLog4j&nbsp;-&nbsp;TestLog4j.TestLog4j<BR>2003-12-19&nbsp;04:19:44:WARN&nbsp;main&nbsp;TestLog4j.TestLog4j2.TestLog4j2&nbsp;-&nbsp;2Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;WARN<BR>2003-12-19&nbsp;04:19:44:ERROR&nbsp;main&nbsp;TestLog4j.TestLog4j2.TestLog4j2&nbsp;-&nbsp;2Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;ERROR<BR>2003-12-19&nbsp;04:19:44:FATAL&nbsp;main&nbsp;TestLog4j.TestLog4j2.TestLog4j2&nbsp;-&nbsp;2Just&nbsp;testing&nbsp;a&nbsp;log&nbsp;message&nbsp;with&nbsp;priority&nbsp;set&nbsp;to&nbsp;FATAL<BR><BR>首先，先来看一下配置文件都有些什么东西。<BR>1)&nbsp;在1区中定义了一个根记录器。这个根记录器具有DEBUG级别并有一个名称为stdout的输出端appender。<BR>2)&nbsp;2区中的内容是这一节的重点，也是应用到记录器层次的地方，但其实也只有两句，充分体现了log4j的简单性。在这里，我们定义了两个名称分别为TestLog4j和TestLog4j.TestLog4j2设计器。<BR>?&nbsp;在定义TestLog4j记录器时没有指定级别，所以它的级别继承自它的父记录器，即要记录器，所以它的级别也为DEBUG。在定义TestLog4j记录器时又定义了一个名称为R的输出端，所以它的输出端有两个，一个从根记录器继承而来的名为stdout的输出端，另一个为在此定义的名为R的输出端。在此需要注意的是，在定义记录器时必须先定义记录器的级别，然后才是记录器的输出端。如果只想定义输出端而不定义级别，则虽然级别可以为空，但逗号分隔符不能省略。如定义TestLog4j记录器的做法。<BR>?&nbsp;在定义TestLog4j.TestLog4j2记录器时又指定了它的级别，由于一个记录器的级别只能有一个，所以新指定的级别将覆写掉它的父记录器的级别（这就象Java中的多态）。我们没有定义TestLog4j.TestLog4j2记录器的输出端，所以它的输出端将从它的父记录器中继承而来。它的父记录器为estLog4j记录器，所以它和estLog4j记录器一样具有两个名称分别为stdout和R的输出端。<BR>3)&nbsp;剩下的3区和4区分别设置了两个输出端的参数值。<BR>接下来，回到我们的代码，看一下是如何取得记录器，在取记录器时又发生了什么。<BR>1)&nbsp;例4-a中的代码（2）中，语句Logger.getLogger()中的参数TestLog4j.class.getName()的值为TestLog4j.&nbsp;TestLog4j，所以此语句的结果是取得一个名为TestLog4j.&nbsp;TestLog4j的记录器的对象。但在配置文件中并没有定义这样的记录器，所以最终将返回与所需的名称TestLog4j.&nbsp;TestLog4j最接近的记录器对象，即名为TestLog4j的记录器的对象。<BR>2)&nbsp;例4-b中的代码（1）的原理与例4-a中的代码（2）相似，期望取得的是名为TestLog4j.TestLog4j2.&nbsp;TestLog4j2的记录器对象，但最终返回的是TestLog4j.TestLog4j2记录器的对象。<BR>三　log4j与J2EE的结合<BR>到目前为止，这篇文章讲的都是如何在application中应用log4j，而Java现在的应用主流是J2EE和J2ME。现在，我们来看一下要如何在J2EE开发中应用log4j。其实在Web&nbsp;application中应用log4j也很简单，与在application中应用log4j不同之处就是要在所有应用log4j的代码之前对log4j进行初始化。所以，我们在web&nbsp;application中就要把log4j的初始化工作独立出来，把它放在Servlet中。下面，我们看一个例子。<BR>例5-a：<BR>进行初始化的Servlet：<BR>import&nbsp;org.apache.log4j.PropertyConfigurator;<BR>import&nbsp;javax.servlet.http.HttpServlet;<BR>import&nbsp;javax.servlet.http.HttpServletRequest;<BR>import&nbsp;javax.servlet.http.HttpServletResponse;<BR><BR>/**<BR>*&nbsp;log4j.jar的初始化类，参考web.xml<BR>*/<BR>public&nbsp;class&nbsp;Log4jInit&nbsp;extends&nbsp;HttpServlet&nbsp;<BR>{<BR>public&nbsp;void&nbsp;init()&nbsp;<BR>{<BR>//通过web.xml来动态取得配置文件<BR>String&nbsp;prefix&nbsp;=&nbsp;getServletContext().getRealPath("/");<BR>String&nbsp;file&nbsp;=&nbsp;getInitParameter("log4j-init-file");<BR><BR>//&nbsp;如果没有给出相应的配置文件，则不进行初始化<BR>if(file&nbsp;!=&nbsp;null)&nbsp;<BR>{<BR>PropertyConfigurator.configure(prefix+file);　//（1）<BR>}<BR>}<BR><BR>public&nbsp;void&nbsp;doGet(HttpServletRequest&nbsp;req,&nbsp;HttpServletResponse&nbsp;res)&nbsp;<BR>{}&nbsp;<BR>}<BR>下面来看一下这个Servlet在web.xml中的定义。<BR>例5-b：<BR>&lt;servlet&gt;<BR>&lt;servlet-name&gt;log4j-init&lt;/servlet-name&gt;<BR>&lt;servlet-class&gt;TestLog4j.Log4jInit&lt;/servlet-class&gt;<BR>&lt;init-param&gt;<BR>&lt;param-name&gt;log4j-init-file&lt;/param-name&gt;<BR>&lt;param-value&gt;sort.properties&lt;/param-value&gt;<BR>&lt;/init-param&gt;<BR>&lt;load-on-startup&gt;1&lt;/load-on-startup&gt;<BR>&lt;/servlet&gt;<BR>因为log4j的初始化要在所有的log4j调用之前完成，所以在web.xml文件中，我们一定要把对应的Servlet定义的load-on-startup应设为1，以便在Web容器启动时即装入该Servlet。<BR>完成了这两个步骤这后，我们就可以象在application开发中一样在web&nbsp;application任何地方应用log4j了。下面是在javabean中的应用的一个例子。<BR>例5-c：<BR>import&nbsp;org.apache.log4j.Logger;<BR><BR>public&nbsp;class&nbsp;InfoForm&nbsp;<BR>{<BR>static&nbsp;Logger&nbsp;logger&nbsp;=&nbsp;Logger.getLogger(InfoForm.class);<BR><BR>protected&nbsp;String&nbsp;title;<BR>protected&nbsp;String&nbsp;content;<BR><BR>public&nbsp;InfoForm()&nbsp;{}<BR><BR>public&nbsp;void&nbsp;setTitle(Object&nbsp;value)&nbsp;<BR>{<BR>logger.debug("nepalon:title&nbsp;=&nbsp;"&nbsp;+&nbsp;title);<BR>title&nbsp;=&nbsp;value;<BR>}<BR><BR>public&nbsp;String&nbsp;getTitle()&nbsp;<BR>{<BR>logger.debug("nepalon:title&nbsp;=&nbsp;"&nbsp;+&nbsp;title);<BR>return&nbsp;title;<BR>}<BR><BR>public&nbsp;void&nbsp;setContent(String&nbsp;value)&nbsp;<BR>{<BR>content&nbsp;=&nbsp;value;<BR>logger.debug("nepalon:&nbsp;content()&nbsp;=&nbsp;"&nbsp;+&nbsp;content);<BR>}<BR><BR>public&nbsp;String&nbsp;getContent()&nbsp;<BR>{<BR>logger.debug("nepalon:&nbsp;content&nbsp;=&nbsp;\n"&nbsp;+&nbsp;content);<BR>return&nbsp;content;<BR>}<BR>} <BR>]]></description>
</item><item>
<title><![CDATA[sqlplus 连接数据库方法]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=newlulu&amp;id=11234</link>
<author>newlulu</author>
<pubDate>2006/1/16 22:38:06</pubDate>
<description><![CDATA[<A></A>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Arial">Sqlplus <A href="mailto:xjdxpt/123456@'(description=(address_list=(address=(proto=tcp)(host=192.168.0.10)(port=1521)))(connect_data=(service_name=smsdb)))">xjdxpt/123456@'(description=(address_list=(address=(proto=tcp)(host=192.168.0.10)(port=1521)))(connect_data=(service_name=smsdb)))</A>’<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /><o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial">或</SPAN><SPAN lang=EN-US style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Arial"><o:p></o:p></SPAN></P><SPAN lang=EN-US style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Tahoma; mso-fareast-font-family: 宋体; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">sqlplus xjdxpt/123456@192.168.0.10:1521/smsdb</SPAN>]]></description>
</item><item>
<title><![CDATA[开发无题:项目管理心得]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=newlulu&amp;id=8686</link>
<author>newlulu</author>
<pubDate>2005/9/18 23:36:01</pubDate>
<description><![CDATA[<a>　今天虽是中秋，但是心情极其郁闷，明天项目就要验收，可是项目的bug一打，是否明早能够解决，走的通，还不能说，至于短信发布和数据统计模块，我现在是不报什么希望了。<br>
&nbsp;&nbsp; 现在反思起来，可能是因为当时对编程的管理制度的制定是不够的，没有可靠的手段来制约程序员，而且没有有力的对违反制度的人员进行处理的手段。看来任何工程的顺利进行都是由制度作为保证的。<br>
</a>]]></description>
</item><item>
<title><![CDATA[开发无题]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=newlulu&amp;id=8616</link>
<author>newlulu</author>
<pubDate>2005/9/14 23:40:18</pubDate>
<description><![CDATA[<a>　在网上查到了这几天一直郁闷的问题，在tomcat下，访问网页出现一个登陆页面，提示语言中有XDB。并且原有的tomcat的8080端口被
占用。经过查找，是因为装上oracle10g的原因。因为，Oracle
9i开始，创建数据库时默认包含了XDB特性。一旦启动了数据库和Listener，Oracle
XDB的http服务就占用8080端口，刚好和JBoss、Tomcat等默认端口冲突。<br>
网上，已有更改XDB端口的方法。见http://blog.csdn.net/DirkSun/services/trackbacks/105509.aspx。<br>
详细解释见下面链接。<br>
<a href="http://www.idevelopment.info/data/Oracle/DBA_tips/xmldb/XMLDB_2.shtml#DisablingthePortAssignment">http://www.idevelopment.info/data/Oracle/DBA_tips/xmldb/XMLDB_2.shtml#DisablingthePortAssignment</a><br>
</a>
<p><br>
1。使用sys登录Oracle，利用dbms_xdb修改端口设置</p>
 
<p>&nbsp; SQL&gt; -- Change the HTTP/WEBDAV port from 8080 to 8081<br>&nbsp; SQL&gt; <b>call dbms_xdb.cfg_update(updateXML(</b><br>&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>&nbsp; dbms_xdb.cfg_get()</b><br>&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>, '/xdbconfig/sysconfig/protocolconfig/httpconfig/http-port/text()'</b><br>&nbsp;&nbsp;&nbsp; 4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>, 8081))</b><br>&nbsp;&nbsp;&nbsp; 5&nbsp; <b>/</b><br><br>&nbsp; Call completed. <br><br><br>&nbsp; SQL&gt; -- Change the FTP port from 2100 to 2111<br>&nbsp; SQL&gt; <b>call dbms_xdb.cfg_update(updateXML(</b><br>&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>&nbsp; dbms_xdb.cfg_get()</b><br>&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>, '/xdbconfig/sysconfig/protocolconfig/ftpconfig/ftp-port/text()'</b><br>&nbsp;&nbsp;&nbsp; 4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>, 2111))</b><br>&nbsp;&nbsp;&nbsp; 5&nbsp; <b>/</b><br><br>&nbsp; Call completed. <br><br><br>&nbsp; SQL&gt; <b>COMMIT;</b><br><br>&nbsp; Commit complete. <br><br><br>&nbsp; SQL&gt; <b>EXEC dbms_xdb.cfg_refresh;</b><br><br>&nbsp; PL/SQL procedure successfully completed.</p>
 
<p><br>2。使用OEM console，选择数据库，XML Database，Configuration。更改XDB的有关设置。</p>
 3。去掉数据库的初始化参数：&nbsp; dispatchers='(PROTOCOL=TCP) (SERVICE=XDB)'，将会禁止XDB的http和ftp服务<a><br>
，里面主要使用三种方法：<br>


		</a>]]></description>
</item><item>
<title><![CDATA[开发无题]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=newlulu&amp;id=8615</link>
<author>newlulu</author>
<pubDate>2005/9/14 23:29:25</pubDate>
<description><![CDATA[<a>　今天系统服务器遇到一处bug，经测试是因为多线程引起。bug出现的现象对同一条记录中的某个字段取值，然后对这个字段累计加1，并更新到数据
库中。由于是多线程的环境下，对这个操作在每个线程中都创建了相应的对象来进行处理。所以导致每个线程环境下，可能得到相同的这个字段数值，在更新数值时
就产生累计的数字和要求的数字不符。因此，对这个对象使用singleton模式进行处理，并且将函数接口使用同步关键字，使得这个对象功能函数支持多线
程环境。经过这种模式处理，bug得到解决。<br>
<br>
</a>]]></description>
</item><item>
<title><![CDATA[开发无题]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=newlulu&amp;id=8470</link>
<author>newlulu</author>
<pubDate>2005/9/11 10:08:33</pubDate>
<description><![CDATA[<a>　开发日志：这两天一直在找系统出现的bug。其表现行为是发送队列只发送了一条记录就被阻塞到发送消息队列。开始以为是阻塞队列
arrayblockingqueue,中的take方法出现问题。但是经过两天的测试，原理是消息队列中保存着两种类型的消息，但是其提取消息的函数不
同，但消息发送线程中只是使用其中的一个函数来提取消息内容，所以有一个消息内容提取不出来，表现的现象是导致发送socket端口失败。<br>
导致这个bug是因为没有做详细设计，没有详细设计的文档复审，这个bug可以在详细设计评审阶段查出。<br>
导致bug不能够很快的准确定位还有因为开发人员，在开发时随意的使用try...catch语句而在catch语句中没有扑捉任何出错有用的信息。导致出现的socket异常不能被显示出来。<br>
</a>]]></description>
</item><item>
<title><![CDATA[开发无题]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=newlulu&amp;id=8459</link>
<author>newlulu</author>
<pubDate>2005/9/10 10:00:12</pubDate>
<description><![CDATA[<a>　解决项目中出现的open cursor too much in
oracle10g中的问题。主要是因为在程序中没有即时释放链接资源。经测试，每次通过JDBC打开链接，在链接里面进行操作时，都会产生一个
cursor，而每次释放链接时只释放了一次资源，所以在循环使用查询语句中，容易产生上述错误。<br>
&nbsp;&nbsp;
产生上述问题还有一个原因就是项目中所写的数据库连接池中没有很好的管理连接回收策略。需要开发者手工的自己来释放连接资源。但是由于每次通过连接池来创
建的连接，需要提供给开发者来进行开发使用。连接池自己本身并不知道开发者在什么时候用完连接资源。所以，这个连接池还有完善的余地。<br>
</a>]]></description>
</item><item>
<title><![CDATA[最近使用displaytag的一些体会]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=newlulu&amp;id=8223</link>
<author>newlulu</author>
<pubDate>2005/9/2 23:34:18</pubDate>
<description><![CDATA[<a>　最近使用DisplayTag这个标记库来处理web页面的显示问题，读了其中的一些源码，也查找了相关的一些文档。对于项目中
（Tomcat.5.4）中的displaytag显示中文问题，在displaytag.properties文件中，使用中文来替换相关的信息，经过
native2ascii -encoding
gbk的编码处理后，保存在class的目录中，基本解决了表格的中文显示问题。对于表格的输出，由于在表格中嵌入了html属性文件，需要在
filter文件中按文档的说明添加mapping *.do，参见displaytag的configuration以及exportfilter说明<br>
（<span style="color: rgb(0, 51, 255);">在Web.xml中</span></a><span style="color: rgb(0, 51, 255);">&lt;filter-mapping&gt;
&lt;filter-name&gt;ResponseOverrideFilter&lt;/filter-name&gt;
&lt;url-pattern&gt;*.do&lt;/url-pattern&gt;</span><br style="color: rgb(0, 51, 255);">
<span style="color: rgb(0, 51, 255);">&lt;/filter-mapping&gt;</span><br style="color: rgb(0, 51, 255);">
<span style="color: rgb(0, 51, 255);">&lt;filter-mapping&gt;</span><br style="color: rgb(0, 51, 255);">
<span style="color: rgb(0, 51, 255);">&nbsp;&lt;filter-name&gt;ResponseOverrideFilter&lt;/filter-name&gt;    &lt;url-pattern&gt;*.jsp&lt;/url-pattern&gt;</span><br style="color: rgb(0, 51, 255);">
<span style="color: rgb(0, 51, 255);">&lt;/filter-mapping&gt;</span><br style="color: rgb(0, 51, 255);">
<span style="color: rgb(0, 51, 255);">）</span><a>。<br>
由于在网页中有checkbox以及其它一些操作链接，而这些内容是不需要输出到文档中的，一开始以为displaytag并没有提供相应的处理功能，所
有打算自己修改。但是经过仔细阅读使用说明，发现displaytagexample中已经提供了使用说明（Based
export），使用Media属性来对需要输出某种格式的文件来屏蔽指定的列数据。<br>
现在只剩下分页的效率问题，关于分页问题的解决，在JIRA网站上以及有个几个版本的Patch，并且cnblogger上的大明白也自己写了封装displaytag的标记库。<br>
<br>
<br>
</a>]]></description>
</item><item>
<title><![CDATA[使用 Jena API 处理 RDF]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=newlulu&amp;id=2886</link>
<author>newlulu</author>
<pubDate>2005/2/17 13:43:19</pubDate>
<description><![CDATA[
<TABLE cellSpacing=0 height=218 style="TABLE-LAYOUT: fixed" width="100%">
<TBODY>
<TR>
<TD style="WORD-WRAP: break-word" vAlign=top><SPAN id=post1 style="COLOR: #000000; FONT-SIZE: 12px">使用 Jena API 处理 RDF 作者：Joe Verzulli 发表时间：2001年5月23日 原文链接：http://www.xml.com/pub/a/2001/05/23/jena.html 译者：dlee 翻译时间：2001年5月26日 对于 RDF (资源定义框架) 的兴趣正在日渐增长，很多用于处理 RDF 的工具和开发库已经被开发出来。这篇文章描述了一个这样的开发库，Jena，用于处理 RDF 的一套 Java API，它也是一个实现这套 API 的开放源码软件的名字。 什么是 RDF？ XML 非常灵活，它允许信息以很多种不同的方式来编码。如果使用有意义的标记，对于人来确定一个 XML 字符串的含义就会相对容易。尽管如此，对于程序来说确定一个 XML 字符串的含义仍然是困难的，因为程序并不懂得英文的标记名。DTD 和 XML Schema 对于这一问题并非真正有帮助，它们只是允许一个程序来验证一个 XML 字符串是否符合某个规则集合。 RDF (RDFMS ，Bray， Ogbuji，SWARDF) 是一个以一种允许程序懂得文档意图表现的含义的方式来表现信息的模型和 XML 语法。它建立在一个 statement，即具有形式{predicate, subject, object} 的一个三元组的概念之上。对于一个三元组的解释是 &lt; subject&gt; 有一个属性 &lt; predicate&gt;，属性值是 &lt; object&gt;。statement 的例子是 {numberOfHits, <A href="http://www.foo.com/index.html," target=_blank>http://www.foo.com/index.html,</A> 3000} 和 {title, <A href="http://bookstore.com/book12," target=_blank>http://bookstore.com/book12,</A> "The Connoisseur's Guide to the Mind"}。在 RDF 中一个 总是一个以 URL 来命名的资源，这个 URL 有一个可选的锚 id。&lt; predicate&gt; 是资源的一个属性，&lt; object&gt; 是资源属性的值。 考虑下面的三元组 (这里 dc 前缀的含义是 Dublin Core) {dc:Publisher, <A href="http://www.w3.org,/" target=_blank>http://www.w3.org,</A> "World Wide Web Consortium"} {dc:Title, <A href="http://www.w3.org,/" target=_blank>http://www.w3.org,</A> "W3C Home Page"} 这些三元组能够被图形化地表示为这样 <A href="http://www.xml.com/2001/05/23/graphics/triples.jpg" target=_blank><A href="http://www.xml.com/2001/05/23/graphics/triples.jpg" target=_blank title=开新窗口浏览><IMG border=0 onload="javascript:if(this.width>screen.width-333)this.width=screen.width-333" src="http://www.xml.com/2001/05/23/graphics/triples.jpg"></A></A><BR><BR>在这个图形中弧线被标记为 predicate。每条弧线从一个代表 subject 的节点出发，终止于一个代表 object 的节点。三元组和图形是同一 RDF 数据模型的两种不同表示。 对于这个模型也有一个 XML 的表示。RDF 要求不同类别的语义信息 (例如，题目，属性和值) 被放置在 XML 中指定的地点。 读取一个 XML 编码的 RDF 程序随后可以确定是否一个特定的元素或属性指示一个主题，一个属性或一个属性的值。 Jena API Jena 由 HP 公司的 Brian McBride 开发，起源于早些时候 SiRPAC API 的工作。Jena 允许你来解析，创建和查询 RDF 模型。 Jena 定义了很多的接口来访问和处理 RDF statement，如下图所示 <A href="http://www.xml.com/2001/05/23/graphics/jenainterface.jpg" target=_blank><A href="http://www.xml.com/2001/05/23/graphics/jenainterface.jpg" target=_blank title=开新窗口浏览><IMG border=0 onload="javascript:if(this.width>screen.width-333)this.width=screen.width-333" src="http://www.xml.com/2001/05/23/graphics/jenainterface.jpg"></A></A><BR><BR>RDFNode 接口为所有的可以作为一个 RDF 三元组一部分的元素提供了一个公共的基础。Literal 接口表示字面值，例如 “red fish” 或 225，可以被用来作为 {predicate, subject, object} 三元组中的 &lt; object&gt;。Literal 接口提供了将字面值转换为诸如 String，int 和 double 这样的 Java 类型的访问方法。 实现 Property 接口的对象可以作为 {predicate, subject, object} 三元组中的 &lt; predicate&gt;。 Statement 接口表示一个 {predicate, subject, object} 三元组。它也可以被用来作为一个三元组中的 &lt; object&gt;，因为 RDF 允许 statement 嵌套。 实现 Container，Alt，Bag 或 Seq 的对象可以作为三元组中的 &lt; object&gt;。 使用 Jena 解析 RDF 一个 RDF 非常有用的领域是在 Web 页面中嵌入元数据。这个 RDF 能够被编码为嵌入在 XHTML 页面中的 XML。 一个了解 RDF 的搜索引擎能够使用这些元数据来给出比一个仅仅靠关键字匹配的搜索引擎给出的更加关联的结果。一个了解 RDF 的搜索引擎爬虫能够使用 Jena 来解析 RDF，这由 Model 接口里的 read() 方法来完成，就象以下代码显示的那样(为清晰起见，例外处理已被省略) File f; FileReader fr; Model model; f = new File("C:\\test1.html"); fr = new FileReader(f); model = new ModelMem(); model.read(fr, RDFS.getURI()); 在这个例子中，C:\test.html 是一个有 RDF 在 元素中的 XHTML 文件。Jena 自动提取 RDF 并忽略 XHTML 的其余部分。解析的结果是一个包含文件中三元组的 RDF 模型。这个模型随后可以被查询。 代码片段中声明之后的前两个语句将 fr 设置为一个与 C:\test.html 相关联的 FileReader。model 被设置为一个 ModelMem 类的实例。ModelMem 是一个 Jena 提供的类，实现 Model 接口，使用主内存来存储模型。其它的实现也是可能的；例如，你可以创建一个基于事务处理数据库的实现。 从一个模型中得到所有的 Statements 一旦一个搜索引擎爬虫创建了一个包含一个 Web 页面元数据的 RDF 模型，它需要添加每个三元组到它的索引中，这样以后搜索就可以找到这个页面。这可以由 Model 接口的 listStatements() 方法来完成。listStatement() 返回一个 StmtIterator 来迭代模型中的每个 statement。它可以象这样来使用 Model model; StmtIterator iter; Statement stmt; . . . iter = model.listStatements(); while (iter.hasNext()) { stmt = iter.next(); // Now use } Statement 接口提供了访问 satement 中 predicate，subject 和 object 的方法，如下所示 Property predicate; Resource subject; RDFNode obj; Statement stmt; . . . subject = stmt.getSubject(); System.out.println("Subject = " + subject.getURI()); predicate = stmt.getPredicate(); System.out.println("Predicate = " +predicate.getLocalName()); obj = stmt.getObject(); System.out.println("Object = " + obj.toString()); 添加 Statement 到一个模型 并非所有应用程序都从 XML 或 XHTML 文件中读取 RDF，很多需要创建基于用户输入或其它数据的 RDF Statement。考虑一个用来维护 email 消息，浏览器书签和日历条目的可查询档案库的 RDF 个人信息管理器。当程序接收到一个新的 email 消息时，它能够提取出发送者和主题，为其创建 RDF 三元组。它也允许用户输入关于消息讨论的主题的信息，并创建包含该主题的 RDF 三元组。 下面的代码演示了 Jena 怎样能够用来在一个模型中创建三元组 Model model; String namespace = "http://www.test.com"; . . . model.createResource("http://www.foo.com/boats#sailboat") .addProperty(model.createProperty(namespace, "length"), 25) .addProperty(model.createProperty(namespace, "color"), "teal"); 这段代码添加了下面的 statements 到模型中 {x:length, <A href="http://www.foo.com/boats#sailboat," target=_blank>http://www.foo.com/boats#sailboat,</A> 25} {x:color, <A href="http://www.foo.com/boats#sailboat," target=_blank>http://www.foo.com/boats#sailboat,</A> "teal"} 这里 x 是一个名域前缀，对应于名域的 URI <A href="http://www.test.com/" target=_blank>http://www.test.com</A> 查询模型 一旦一个 RDF 模式被创建，我们需要一种方法来查询它，例如，考虑一个包含 RDF 元数据的旅游 FAQ。假设我们想找到所有与非洲旅行有关的问题，换句话说我们希望找到在模型中所有具有形式 {destination, res, Africa} 的 res 的值。下面的代码显示怎样完成这件事。它任意假设属性的目的地是在一个 <A href="http://foo.org/" target=_blank>http://foo.org</A> 的名域中。 Model model; Resource r; ResIterator resourceIter; . . . resourceIter = model.listSubjectsWithProperty( model.createProperty("http://foo.org/destination"), "Africa"); while (resourceIter.hasNext()) { r = resourceIter.next(); System.out.println("Resource " + r.toString() + " is about travel to Africa"); } listSubjectsWithProperty(p, v) 为任何 &lt; subject&gt; 查找所有的 {p, &lt; subject&gt;, v} 形式的三元组。它返回一个迭代匹配的三元组对象。 哪里能够得到 Jena Jena 可以从这里下载 <A href="http://www.hpl.hp.co.uk/people/bwm/rdf/jena/download.htm" target=_blank>http://www.hpl.hp.co.uk/people/bwm/rdf/jena/download.htm</A> 。下载文件包括一些例子，JavaDoc，源代码和 jar 文件。 致谢 感谢 Brian McBride 为这篇文章的草稿所写的富有帮助的评论和创建了 Jena。 --dlee 有人说一个人有烦恼是因为记性太好。那年开始，我忘记了很多事情，唯一有印象的，就是我喜欢桃花。 <BR></SPAN></TD>
<TD width=16></TD></TR>
<TR>
<TD></TD>
<TD vAlign=bottom><BR></TD></TR></TBODY></TABLE>]]></description>
</item><item>
<title><![CDATA[mygrid剖析---feta ---fetaEngine]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=newlulu&amp;id=2850</link>
<author>newlulu</author>
<pubDate>2005/2/15 11:28:18</pubDate>
<description><![CDATA[<h1><font size="3"><span lang="EN-US">Store</span><span style="font-family: 宋体;">包</span><span lang="EN-US">/</span></font></h1>
<p class="MsoNormal"><b><span style="font-family: 宋体;">此包下保存与文件有关的类，负责文件的读取，分析，基于</span><span lang="EN-US">rdql</span></b><b><span style="font-family: 宋体;">的查询等。</span><span lang="EN-US"><o:p></o:p></span></b></p>
<p class="MsoNormal"><span lang="EN-US">FetaLoad</span><span style="font-family: 宋体;">类，负责将</span><span lang="EN-US">URL</span><span style="font-family: 宋体;">目录下的文件导入到</span><span lang="EN-US">XML</span><span style="font-family: 宋体;">的</span><span lang="EN-US">dom</span><span style="font-family: 宋体;">数组中。其中</span><span lang="EN-US">URL</span><span style="font-family: 宋体;">可以为本地目录和网络目录，如果为网络目录则使用</span><span lang="EN-US">WebCrawler</span><span style="font-family: 宋体;">类将得到制定网络目录下的文件名并将其转化为</span><span lang="EN-US">dom</span><span style="font-family: 宋体;">数组。</span></p>
<p class="MsoNormal"><span lang="EN-US">FetaRDFSParser</span><span style="font-family: 宋体;">类，负责分析</span><span lang="EN-US">RDFS</span><span style="font-family: 宋体;">类型的本体文件，并将其中的</span><span lang="EN-US">class</span><span style="font-family: 宋体;">标签下的</span><span lang="EN-US">about</span><span style="font-family: 宋体;">属性按树型结构进行组织</span><span lang="EN-US">(</span><span style="font-family: 宋体;">即使用树型结构表示</span><span lang="EN-US">subclassof</span><span style="font-family: 宋体;">关系</span><span lang="EN-US">)</span><span style="font-family: 宋体;">，以此结构作为一些</span><span lang="EN-US">UI</span><span style="font-family: 宋体;">组件的本体（</span><span lang="EN-US">RDFS</span><span style="font-family: 宋体;">）显示结构。</span></p>
<p class="MsoNormal"><span lang="EN-US">JenaConvenience</span><span style="font-family: 宋体;">类封装了</span><span lang="EN-US">jena</span><span style="font-family: 宋体;">类包中的</span><span lang="EN-US">rdql</span><span style="font-family: 宋体;">查询功能。里面的主要函数为</span><span lang="EN-US">Vector processQuery (Model m, String rdqlQuery, String variableName) </span><span style="font-family: 宋体;">，</span><span lang="EN-US">Returns a vector of resources matching variableName in rdqlQuery. This method will execute rdqlQuery on the triple store.<span style="">&nbsp; </span>We assume that the query contains a select statement over a single variableName. The result will be returned in a Vector.</span></p>
<p class="MsoNormal"><span lang="EN-US">XMLConverter</span><span style="font-family: 宋体;">类用于将</span><span lang="EN-US">XML</span><span style="font-family: 宋体;">文件转化为</span><span lang="EN-US">RDF</span><span style="font-family: 宋体;">文件。（</span><span lang="EN-US">Utility
to convert an XML file to RDF, very limited capability. Assumes a tree
structured form with simple element to property mappings, not
namespace-aware. To set up the mapping you register XML element names
with instances of XMLNodeConverter. The instances are either custom
code or ground versions of ConvertLiteral, ConvertURI, ConvertList,
ConvertClass.</span><span style="font-family: 宋体;">）</span></p>
<p class="MsoNormal"><span lang="EN-US">PedroXMLToRDF</span><span style="font-family: 宋体;">类，通过使用</span><span lang="EN-US">XMLConverter</span><span style="font-family: 宋体;">类对</span><span lang="EN-US">services</span><span style="font-family: 宋体;">文件中的元素转化为</span><span lang="EN-US">jena</span><span style="font-family: 宋体;">中的</span><span lang="EN-US">module</span><span style="font-family: 宋体;">。</span></p>
<p class="MsoNormal"><span lang="EN-US">PedroXMLWrapper</span><span style="font-family: 宋体;">类分析</span><span lang="EN-US">services</span><span style="font-family: 宋体;">文件，得到对</span><span lang="EN-US">web services</span><span style="font-family: 宋体;">描述的各种属性。</span></p>
<p class="MsoNormal"><span lang="EN-US">Service</span><span style="font-family: 宋体;">类，以</span><span lang="EN-US">RDF</span><span style="font-family: 宋体;">格式来保存</span><span lang="EN-US">services</span><span style="font-family: 宋体;">的属性描述。</span></p>
<h1><font size="4"><span lang="EN-US">Annotator</span><span style="font-family: 宋体;">包</span></font></h1>
<p class="MsoNormal"><span lang="EN-US">SoaplabToSkeletonConverter</span><span style="font-family: 宋体;">类负责分析</span><span lang="EN-US">Soaplab</span><span style="font-family: 宋体;">类型服务文件，。</span></p>
<p class="MsoNormal"><span lang="EN-US">WSDLToSkeletonConverter</span><span style="font-family: 宋体;">类负责分析</span><span lang="EN-US">WSDL</span><span style="font-family: 宋体;">类型的文件</span></p>
<p class="MsoNormal"><span lang="EN-US">WSDLToSkeletonConverterXSLT</span><span style="font-family: 宋体;">类，</span><span lang="EN-US">Convert the WSDL file to a skeleton XML file using an XSLT Script</span><span style="font-family: 宋体;">。</span></p>
<p class="MsoNormal"><span lang="EN-US">DOMUtil</span><span style="font-family: 宋体;">类，包含两个函数，</span><span lang="EN-US">WriteOut</span><span style="font-family: 宋体;">负责将</span><span lang="EN-US">dom</span><span style="font-family: 宋体;">以</span><span lang="EN-US">xml</span><span style="font-family: 宋体;">格式写入到指定文件中。</span><span lang="EN-US">GetSerializerForDoc</span><span style="font-family: 宋体;">用来得到代写入的</span><span lang="EN-US">document</span><span style="font-family: 宋体;">的</span><span lang="EN-US">xml</span><span style="font-family: 宋体;">序列化格式。</span></p>
<p class="MsoNormal"><span lang="EN-US">CommonModel</span><span style="font-family: 宋体;">类用来保存描述一般模型的字符串常量。</span></p>
<h1><font size="3"><span lang="EN-US">Command</span><span style="font-family: 宋体;">包</span></font></h1>
<p class="MsoNormal"><span lang="EN-US">AbstractCommand</span><span style="font-family: 宋体;">类为各种查找命令的抽象类。此包内其他各个查找命令类分别继承于此类，只是将查找字符串做出相应的修改，以适应查找不同对象的需要。查找字符串的形式为</span><span lang="EN-US">jena</span><span style="font-family: 宋体;">中的</span><span lang="EN-US">rdql</span><span style="font-family: 宋体;">格式。</span></p>
<h1><font size="4"><span lang="EN-US">Util</span><span style="font-family: 宋体;">包</span></font></h1>
<p class="MsoNormal"><span lang="EN-US">AbstractEnumeration</span><span style="font-family: 宋体;">类，</span><span lang="EN-US">Provides
support for Enumerated Types in Java. This class provides several
methods useful for all Enumerated Types including a sensible printable
toString method, the total number of instances of a given type, an
Iterator through all the types, and an ordinal number running from 0
upwards for each type.</span></p>
<p class="MsoNormal"><span lang="EN-US">StringUtil</span><span style="font-family: 宋体;">类，提供各种删除字符串中空格的方法。</span></p>
<p class="MsoNormal"><span lang="EN-US">WSDLResolver</span><span style="font-family: 宋体;">类，负责解析</span><span lang="EN-US">WSDL</span><span style="font-family: 宋体;">格式的文件。</span></p>
<h1><font size="3"><span lang="EN-US">WebServices</span><span style="font-family: 宋体;">包</span></font></h1>
<p class="MsoNormal"><span style="font-family: 宋体;">此包内为一个简单的</span><span lang="EN-US">WebService</span><span style="font-family: 宋体;">的实现。</span></p>
<h1><font size="4"><span lang="EN-US">Client/</span><span style="font-family: 宋体;">包</span></font></h1>
<p class="MsoNormal"><span style="font-family: 宋体;">此包下的类保存与界面有关的类。</span></p>
<p class="MsoNormal"><span lang="EN-US">&nbsp;<o:p></o:p></span></p>]]></description>
</item><item>
<title><![CDATA[check out 完mygrid源码。]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=newlulu&amp;id=2145</link>
<author>newlulu</author>
<pubDate>2005/1/21 9:21:30</pubDate>
<description><![CDATA[
<P>今日cheng out 完mygrid源码，等寒假有空要分析一下。需要声明mygrid在网上有两个版本，一个好像是德国一个大学搞得，作为分布式算法的运行平台，俺车chenkout的是英国的mygrid项目，这个在知识网格界较有名。</P>
<P>&nbsp;&nbsp; &nbsp;明天终于可以回北京了，到南方两周来一直上火，可能是北方住习惯了。回去向老板交差，然后抓紧写论文。如果可能的话，我将会陆续公布我对mygrid的一些剖析结果和体会。</P>]]></description>
</item><item>
<title><![CDATA[今天建站]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=newlulu&amp;id=2104</link>
<author>newlulu</author>
<pubDate>2005/1/20 10:35:36</pubDate>
<description><![CDATA[<p>这我的第一篇日志，我计划在我的bloger上记录我的心情和我感兴趣的技术上一些学习心得。本人研究所涉猎的范围广泛，常有“泛
而不精”之闲，这可能是我予生据来的性格吧，下面是我对计算机方面所涉足的内容，计算智能，分布式式系统，知识管理，数据挖掘。已做过的软件也是五花八
门，有使用各种计算智能算法对TSP问题的解决，用j2ee＋struts＋oracle建过各种网站，使用C/C++开发过基于SIP协议的即时通讯系
统，也开发过POS机系统，使用Domino/Lotus开发过几个OA系统...，包括加上毕业设计使用的powerbuilder，也可是开发工具使
用上占了一箩筐。</p>
<p>&nbsp;&nbsp;&nbsp; 先写到这吧，现在正在做论文，研究的课题为知识网格一类的内容，希望大家能够交流。</p>
<p>&nbsp;&nbsp;&nbsp; </p>]]></description>
</item>
</channel>
</rss>