﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-桃源谷</title><link>http://www.cppblog.com/lymons/</link><description>&lt;h1&gt;&lt;b&gt;心灵的旅行&lt;/b&gt;&lt;/h1&gt;&lt;/p&gt;
人生就是一场旅行，不在乎旅行的目的地，在乎的是沿途的风景和看风景的心情 !</description><language>zh-cn</language><lastBuildDate>Fri, 10 Apr 2026 07:10:32 GMT</lastBuildDate><pubDate>Fri, 10 Apr 2026 07:10:32 GMT</pubDate><ttl>60</ttl><item><title>C++ 局部静态初始化不是线程安全的！</title><link>http://www.cppblog.com/lymons/archive/2010/08/01/120638.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Sun, 01 Aug 2010 05:19:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2010/08/01/120638.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/120638.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2010/08/01/120638.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/120638.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/120638.html</trackback:ping><description><![CDATA[<span style="font-family: Verdana,Arial,Helvetica,sans-serif; font-size: 14px;">
<h3 class="post-name" style="margin: 7px 0px 18px; padding: 0px; font-weight: bold; color: #333333; font-size: 1.2em; text-decoration: none;">请注意,C++ 局部静态初始化不是线程安全!</h3>
http://blogs.msdn.com/b/oldnewthing/archive/2004/03/08/85901.aspx
<br><br>
<div class="post-date" style="padding: 6px 0px; font-size: 12px; color: #666666; font-weight: bold; position: relative; display: inline;"><span class="value" style="margin-left: 3px;">8 Mar 2004 7:00 AM&nbsp;</span></div>
<div class="post-attributes" style="padding: 6px 0px; display: inline; border-left-color: #cccccc; margin-left: 8px;">
<ul class="attribute-list" style="margin: 0px 0px 0px 8px; padding: 0px; list-style-type: none; display: inline; background-image: url(&quot;http://blogs.msdn.com/themes/wireframe/Images/icon-sprite.gif&quot;); background-color: transparent; background-position: 0px -380px;">
    <li class="attribute-item post-reply-count" style="margin: 0px 3px 0px 0px; padding: 0px; display: inline;"><span class="attribute-value" style="font-size: 16px; font-weight: bold; color: #005ae1; margin-left: 18px;"><a href="http://blogs.msdn.com/b/oldnewthing/archive/2004/03/08/85901.aspx#comments" class="internal-link view-replies" style="outline-style: none; color: #770000; text-decoration: underline; font-weight: inherit;"><span></span>49</a></span></li>
</ul>
</div>
<div class="post-content user-defined-markup" style="margin: 8px 0px 10px; padding: 10px 0px; overflow: hidden; max-width: 100%; position: relative; line-height: 1.5em;">
<p style="margin-top: 12px;">在块作用域中的静态变量的规则 (与之相对的是全局作用域的静态变量) 是, 程序第一次执行到他的声明的时候进行初始化.</p>
<p style="margin-top: 12px;">察看下面的竞争条件:</p>
<pre>int ComputeSomething()
{
&nbsp; &nbsp;static int cachedResult = ComputeSomethingSlowly();
&nbsp; &nbsp;return cachedResult;
}
</pre>
<p style="margin-top: 12px;">这段代码的意图是在该函数第一次被调用的时候去计算一些费用, 并且把结果缓冲起来待函数将来再被调用的时候则直接返回这个值即可.</p>
<p style="margin-top: 12px;">这个基本技巧的变种,在网络上也被叫做 <a href="http://users.utu.fi/~sisasa/oasis/cppfaq/ctors.html#[10.9]" style="outline-style: none; color: #770000; text-decoration: underline; font-weight: inherit;">避免 "static initialization order fiasco"</a>. ( fiasco这个词 在这个网页上有非常棒的描述,因此我建议大家去读一读然后去理解它.)</p>
<p style="margin-top: 12px;">这段代码的问题是非线程安全的. 在局部作用域中的静态变量是编译时会在编译器内部转换成下面的样子:</p>
<pre>int ComputeSomething()
{
<font color="blue">  static bool cachedResult_computed = false;
&nbsp; static int cachedResult;
&nbsp; if (!cachedResult_computed) {
&nbsp; &nbsp; &nbsp;cachedResult_computed = true;
&nbsp; &nbsp; &nbsp;cachedResult = ComputeSomethingSlowly();
&nbsp; }</font>
&nbsp; return cachedResult;
}
</pre>
<p style="margin-top: 12px;">现在竞争条件就比较容易看到了.</p>
<p style="margin-top: 12px;">假设两个线程在同一时刻都调用这个函数. 第一个线程在执行 cachedResult_computed&nbsp;=&nbsp;true 后, 被抢占. 第二个线程现在看到的 cachedResult_computed 是一个真值( true ),然后就略过了if分支的处理,最后该函数返回的是一个未初始化的变量.</p>
<p style="margin-top: 12px;">现在你看到的东西并不是一个编译器的bug, 这个行为 <strong>C++ 标准所要求的.</strong></p>
<p style="margin-top: 12px;">你也能写一个变体来产生一个更糟糕的问题:</p>
<pre>class Something { ... };
int ComputeSomething()
{
&nbsp; &nbsp;static Something s;
&nbsp; &nbsp;return s.ComputeIt();
}
</pre>
<p style="margin-top: 12px;">同样的在编译器内部它会被重写 (这次, 我们使用C++伪代码):</p>
<pre>class Something { ... };
int ComputeSomething()
{
<font color="blue">  static bool s_constructed = false;
&nbsp; static uninitialized Something s;
&nbsp; if (!s_constructed) {
&nbsp; &nbsp; &nbsp; s_constructed = true;
&nbsp; &nbsp; &nbsp; new(&amp;s) Something; // construct it
&nbsp; &nbsp; &nbsp; atexit(DestructS);
&nbsp; }</font>
&nbsp; return s.ComputeIt();
}
<font color="blue">// Destruct s at process termination
void DestructS()
{
&nbsp; &nbsp;ComputeSomething::s.~Something();
}</font>
</pre>
<p style="margin-top: 12px;">注意这里有多重的竞争条件. 就像前面所说的, 一个线程很可能在另一个线程之前运行并且在"s"还没有被构造前就使用它. <br></p>
<p style="margin-top: 12px;">甚至更糟糕的情况, 第一个线程很可能在s_contructed 条件判定 之后,在他被设置成"true"<span style="font-weight: bold;">之前</span>被抢占. 在这种场合下, 对象s就会被<span style="font-weight: bold;">双重构造</span>和<span style="font-weight: bold;">双重析构</span>.&nbsp;</p>
<p style="margin-top: 12px;">这样就不是很好.</p>
<p style="margin-top: 12px;">但是等等, 这并不是全部, 现在(原文是Not,我认为是Now的笔误)看看如果有<span style="font-style: italic;">两个</span>运行期初始化局部静态变量的话会发生什么: <br></p>
<pre>class Something { ... };
int ComputeSomething()
{
static Something s(0);
static Something t(1);
return s.ComputeIt() + t.ComputeIt();
}
</pre>
<p style="margin-top: 12px;">上面的代码会被编译器转化为下面的伪C++代码:</p>
<pre>class Something { ... };
int ComputeSomething()
{
<font color="blue">  static char constructed = 0;
static uninitialized Something s;
if (!(constructed &amp; 1)) {
constructed |= 1;
new(&amp;s) Something; // construct it
atexit(DestructS);
}
static uninitialized Something t;
if (!(constructed &amp; 2)) {
constructed |= 2;
new(&amp;t) Something; // construct it
atexit(DestructT);
}</font>
return s.ComputeIt() + t.ComputeIt();
}
</pre>
<p style="margin-top: 12px;">为了节省空间, 编译器会把两个"x_constructed" 变量放到一个 bitfield 中. 现在这里在变量"construted"上就有多个<span style="font-weight: bold;">无内部锁定</span>的读-改-存操作.</p>
<p style="margin-top: 12px;">现在考虑一下如果一个线程尝试去执行 "constructed |= 1", 而在同一时间另一个线程尝试执行 "constructed |= 2".</p>
<p style="margin-top: 12px;">在x86平台上, 这条语句会被汇编成</p>
<pre>  or constructed, 1
...
or constructed, 2
</pre>
并没有 "lock" 前缀. 在多处理机器上, 很有可能发生两个存储都去读同一个旧值并且互相使用冲突的值进行碰撞(clobber).
<p style="margin-top: 12px;">在 ia64 和 alpha平台上, 这个碰撞将更加明显,因为它们么没有这样的<span style="font-weight: bold;">读-改-存</span>的单条指令; 而是被编码成三条指令:</p>
<pre>  ldl t1,0(a0)     ; load
addl t1,1,t1     ; modify
stl t1,1,0(a0)   ; store
</pre>
<p style="margin-top: 12px;">如果这个线程在 load 和 store之间被抢占, 这个存储的值可能将不再是它曾经要写入的那个值.</p>
<p style="margin-top: 12px;">因此,现在考虑下面这个有问题的执行顺序:</p>
<ul>
    <li>线程A 在测试 "constructed" 条件后发现他是零, 并且正要准备把这个值设定成1， 但是它被抢占了.</li>
    <li>线程B 进入同样的函数, 看到 "constructed" 是零并继续去构造 "s" 和 "t", 离开时 "constructed" 等于3.</li>
    <li>线程A 继续执行并且完成它的 读-改-存 的指令序列, 设定 "constructed" 成 1, 然后构造 "s" (第二次).</li>
    <li>线程A 然后继续去构造 "t" (第二次) 并设定 "constructed" (最终) 成 3.</li>
</ul>
<p style="margin-top: 12px;">现在, 你可能会认为你能用临界区 (critical section) 来封装这个运行期初始化动作:</p>
<pre>int ComputeSomething()
{
EnterCriticalSection(...);
static int cachedResult = ComputeSomethingSlowly();
LeaveCriticalSection(...);
return cachedResult;
}
</pre>
<p style="margin-top: 12px;">因为你现在把这个一次初始化放到了临界区里面,而使它线程安全.</p>
<p style="margin-top: 12px;">但是如果从同一个线程再一次调用这个函数会怎样? ("我们跟踪了这个调用; 它确实是来自这个线程!") 如果 ComputeSomethingSlowly() 它自己间接地调用 ComputeSomething()就会发生这个状况.</p>
<p style="margin-top: 12px;">结论: 当你看见一个局部静态变量在运行期初始化时, 你一定要小心.</p>
</div>
</span><img src ="http://www.cppblog.com/lymons/aggbug/120638.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2010-08-01 13:19 <a href="http://www.cppblog.com/lymons/archive/2010/08/01/120638.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用C++编写synchronized method比较难</title><link>http://www.cppblog.com/lymons/archive/2010/07/17/118502.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Sat, 17 Jul 2010 04:17:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2010/07/17/118502.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/118502.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2010/07/17/118502.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/118502.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/118502.html</trackback:ping><description><![CDATA[<h3>在C++下编写synchronized method比较难 (1)<span class="hatena-star-comment-container"><img class="hatena-star-comment-button" title="Comments" style="border: medium none ; margin: 0pt; padding: 0pt; display: none; vertical-align: middle; cursor: pointer;" tabindex="0" alt="Comments" src="http://s.hatena.ne.jp/images/comment.gif"></span><span class="hatena-star-star-container"><img class="hatena-star-add-button" title="Add star" style="border: medium none ; margin: 0pt 3px; padding: 0pt; vertical-align: middle; cursor: pointer;" tabindex="0" alt="Add star" src="http://s.hatena.ne.jp/images/add.gif"></span></h3>
<p>在Java中有叫做synchronized这样一个方便的关键字。使用这个关键字的话,就可以像下面那样能够简单的进行"同步"method. 然而,被同步的method并不表示它就能在多线程中同时被执行. <br></p>
<pre>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: #eeeeee;"><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;Foo&nbsp;{&nbsp;&nbsp;<img src="http://www.cppblog.com/Images/dot.gif">&nbsp;&nbsp;<br></span><span style="color: #0000ff;">&nbsp;&nbsp;&nbsp;&nbsp; public</span><span style="color: #000000;">&nbsp;synchronized&nbsp;boolean&nbsp;getFoo()&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img src="http://www.cppblog.com/Images/dot.gif">&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp; }</span></div>
<br></pre>
<p>那么、在C++ (with pthread)中如何能实现同样的功能呢? 首先,有一个最简单的方法就是下面这个.</p>
<pre>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: #eeeeee;"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;方法&nbsp;a</span><span style="color: #008000;"><br></span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;Foo::need_to_sync(</span><span style="color: #0000ff;">void</span><span style="color: #000000;">)&nbsp;</span><span id="Codehighlighter1_37_182_Closed_Text" style="border: 1px solid #808080; display: none; background-color: #ffffff;"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id="Codehighlighter1_37_182_Open_Text"><span style="color: #000000;">{&nbsp;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"></span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;pthread_mutex_t&nbsp;mutex&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;PTHREAD_MUTEX_INITIALIZER;&nbsp;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top">pthread_mutex_lock(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">mutex);&nbsp;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"></span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;临界区处理&nbsp;&nbsp;</span><span style="color: #008000;"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"></span><span style="color: #000000;">pthread_mutex_unlock(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">mutex);&nbsp;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"></span><span style="color: #0000ff;">return</span><span style="color: #000000;">;<br>}</span></span></div>
<br></pre>
<p><br>这个方法, 暂且不说C语言, 就是在C++中下面的若干问题 <br></p>
<ul>
    <li>在临界区中间被return出来
    </li>
    <li>在临界区中间发生异常exception<br></li>
</ul>
<p>发生的场合, mutex没有被解锁unlock。我们可以像下面代码那样对这点进行改进.</p>
<pre>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: #eeeeee;"><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;方法&nbsp;b</span><span style="color: #008000;"><br></span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;ScopedLock&nbsp;:&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;boost::noncopyable&nbsp;{<br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">:&nbsp;&nbsp;</span><span style="color: #0000ff;">explicit</span><span style="color: #000000;">&nbsp;ScopedLock(pthread_mutex_t</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">&nbsp;m)&nbsp;:&nbsp;m_(m)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;pthread_mutex_lock(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">m_);<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;</span><span style="color: #000000;">~</span><span style="color: #000000;">ScopedLock(pthread_mutex_t</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">&nbsp;m)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;pthread_mutex_unlock(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">m_);<br>&nbsp;&nbsp;}<br></span><span style="color: #0000ff;">private</span><span style="color: #000000;">:<br>&nbsp;&nbsp;pthread_mutex_t</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">&nbsp;m_;<br>};<br><br></span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;Foo::need_to_sync(</span><span style="color: #0000ff;">void</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;pthread_mutex_t&nbsp;mutex&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;PTHREAD_MUTEX_INITIALIZER;<br>&nbsp;&nbsp;{<br>&nbsp;</span><span style="color: #008000;">   //</span><span style="color: #008000;">&nbsp;虽然不加这个括号程序也没有问题。</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;ScopedLock&nbsp;</span><span style="color: #0000ff;">lock</span><span style="color: #000000;">(mutex);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;在此处添加处理</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;}<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">;<br>}</span></div>
<br></pre>
<p>OK。return和异常的问题就可以解决了. 但是, 上面并没有完全解决这个问题,仍然有下面这个问题.</p>
<ol>
    <li>使用这个pthread_mutex_t并不是C++的.特别是存在下面的问题:
    <ul>
        <li>不能和其他的Mutex类型做同样的处理
        </li>
        <li>与其他的Mutex类型使用同一个ScopedLock类,则不能lock </li>
    </ul>
    </li>
    <li>Java的synchronized方法虽然可以"递归lock", 但是上面的代码并不是这样. 在临界区中递归调用自己的话就会发生死锁. </li>
</ol>
<p>特别是,第2点的递归lock的问题是很重要的. 这里好好地使用glibc扩展的话就可以象下面那样解决.</p>
<pre>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><span style="color: #008000;">/</span><span style="color: #008000;">&nbsp;方法&nbsp;c</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br></span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;Foo::need_to_sync(</span><span style="color: #0000ff;">void</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;pthread_mutex_t&nbsp;mutex&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;PTHREAD_MUTEX_RECURSIVE_INITIALIZER_NP;</span></div>
<br></pre>
<p>从NP<span class="footnote"><a title="non portable の意" href="http://d.hatena.ne.jp/yupo5656/20040713#20040713f1" name="20040713fn1">*1</a>这个后缀名就知道, 这个方法没有可移植性. 必须使用</span>pthread_mutex_init来初始化递归mutex,而pthread_mutex_init函数在一个线程中只能被调用一次. 如果想要用synchronized method的方法来实现这个的话,就变成了"是先有鸡还是先有蛋"的话题了. 所以,用叫做pthread_once的函数来实现它,这也是<a href="http://www.opengroup.org/onlinepubs/009695399/functions/pthread_mutex_init.html#tag_03_537_08_04">在SUSv3中被记载的定则</a>。</p>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;方法&nbsp;d</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br></span><span style="color: #0000ff;">namespace</span><span style="color: #000000;">&nbsp;</span><span style="color: #008000;">/*</span><span style="color: #008000;">&nbsp;anonymous&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;">&nbsp;{<br>&nbsp;&nbsp;pthread_once_t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;once&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;PTHREAD_ONCE_INIT;<br>&nbsp;&nbsp;pthread_mutex_t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mutex;<br>&nbsp;&nbsp;pthread_mutexattr_t&nbsp;attr;<br><br>&nbsp;&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;mutex_init()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;pthread_mutexattr_init(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">attr);<br>&nbsp;&nbsp;&nbsp;&nbsp;pthread_mutexattr_settype(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">attr,&nbsp;PTHREAD_MUTEX_RECURSIVE);<br>&nbsp;&nbsp;&nbsp;&nbsp;pthread_mutex_init(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">mutex,&nbsp;</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">attr);<br>&nbsp;&nbsp;}<br>}<br><br></span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;Foo::need_to_sync(</span><span style="color: #0000ff;">void</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;pthread_once(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">once,&nbsp;mutex_init);<br><br><br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;ScopedLock&nbsp;</span><span style="color: #0000ff;">lock</span><span style="color: #000000;">(mutex);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;処理</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">;<br>}</span></div>
<br>
<p>上面的代码就OK了。</p>
<p><br></p>
<p>这就能够解决递归lock的问题了.但是..., 这个方法</p>
<ul>
    <li>这越来越不像C++的代码了.。对每一个想要同步(synchronize)的方法都像这么样写代码的话,效率变得非常低下.
    </li>
    <li>随机成本大。速度慢。 </li>
</ul>
<p>就会产生上面那样的新问题.</p>
<br>
<div class="section">
<h3><a href="http://d.hatena.ne.jp/yupo5656/20040713/p2" name="p2"><span class="sanchor">■</span></a>[<a class="sectioncategory" href="http://d.hatena.ne.jp/yupo5656/searchdiary?word=%2A%5BC%2B%2B%5D">C++</a>] 在C++下编写synchronized method比较难(2)<span class="hatena-star-comment-container"><img class="hatena-star-comment-button" title="Comments" style="border: medium none ; margin: 0pt; padding: 0pt; display: none; vertical-align: middle; cursor: pointer;" tabindex="0" alt="Comments" src="http://s.hatena.ne.jp/images/comment.gif"></span><span class="hatena-star-star-container"><img class="hatena-star-add-button" title="Add star" style="border: medium none ; margin: 0pt 3px; padding: 0pt; vertical-align: middle; cursor: pointer;" tabindex="0" alt="Add star" src="http://s.hatena.ne.jp/images/add.gif"></span></h3>
<p>"不, 方法的同步应该是经常必需的, 并不是没有方便的办法",这样的说法也有吧. 是的, 有. 一般的办法是下面那样,</p>
<ul>
    <li>做成一个Mutex,作为(non-POD型的, 即普通的)C++类
    </li>
    <li>做成一个Mutex类的实例,作为类变量, 或者是全局变量, 来同步化方法</li>
</ul>
<br>
<p>来看看它的具体实现吧. 首先做成的Mutex类是下面那样<span class="footnote"><a href="http://d.hatena.ne.jp/yupo5656/20040713#20040713f2" title="再帰mutexにする例は冗長なので略します。pthread_mutex_initによる初期化を行っていますので、再帰mutex化す
るのは容易です。" name="20040713fn2">*2</a></span>。</p>
<pre>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;Mutex&nbsp;{<br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br>&nbsp;&nbsp;Mutex()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;pthread_mutex_init(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">m_,&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">);<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;Lock(</span><span style="color: #0000ff;">void</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;pthread_mutex_lock(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">m_);<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;Unlock(</span><span style="color: #0000ff;">void</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;pthread_mutex_unlock(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">m_);<br>&nbsp;&nbsp;}<br></span><span style="color: #0000ff;">private</span><span style="color: #000000;">:<br>&nbsp;&nbsp;pthread_mutex_t&nbsp;m_;<br>};</span></div>
<br></pre>
<p>现在的Mutex类,被作为抽象基类(接口类)的场合也比较多. 在这里就不说了. ScopedLock类也需要有若干的修改. 想下面那样写就好.</p>
<pre>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">template</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">typename&nbsp;T</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;ScopedLock&nbsp;{<br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br>&nbsp;&nbsp;ScopedLock(T</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">&nbsp;m)&nbsp;:&nbsp;_m(m)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;_m.Lock();<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;</span><span style="color: #000000;">~</span><span style="color: #000000;">ScopedLock()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;_m.Unlock();<br>&nbsp;&nbsp;}<br></span><span style="color: #0000ff;">private</span><span style="color: #000000;">:<br>&nbsp;&nbsp;T</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">&nbsp;_m;<br>};</span></div>
<br></pre>
<p>用这个Mutex类来同步方法, 就可以像下面那样写. 首先是看看一个明显的有错误的例子.</p>
<pre>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;方法e</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br></span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;Foo::need_to_sync(</span><span style="color: #0000ff;">void</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;Mutex&nbsp;mutex;<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;ScopedLock</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Mutex</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">lock</span><span style="color: #000000;">(mutex);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;処理</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">;<br>}</span></div>
<br></pre>
<p>这是... 代码虽然简单易懂,但是很遗憾,它不能很好工作. NG!. Foo::need_to_sync函数第一次被执行的时候如果恰好是多个线程同时执行的话, mutex 的构造函数就有被多次调用的可能性.关于理由，可以参考微软中比较有名气的blog文章The Old New Thing、<a href="http://blogs.msdn.com/oldnewthing/archive/2004/03/08/85901.aspx">"C++ scoped static initialization is not thread-safe, on purpose!"</a>在这里面有详尽的描述，所以就我们就不在详细叙述了<span class="footnote"><a title="そのうちg++ -s結果とその解説でも載せます" href="http://d.hatena.ne.jp/yupo5656/20040713#20040713f3" name="20040713fn3">*3</a></span>。在这篇blog里使用了VC++的代码作为例子，但是g++也是差不多的。所以<strong>&#8220;动态的初始化局部的静态变量&#8221;是， 在线程所完全意识不到的情况下进行的<span class="footnote"><a href="http://d.hatena.ne.jp/yupo5656/20040713#20040713f4" title="2005/12追記:
最近のg++では例外アリ、http://d.hatena.ne.jp/yupo5656/20051215/p2 を参照のこと" name="20040713fn4">*4</a></span></strong>。</p>
<br>
<p>接下来，要介绍一个在目前做的比较好的方法。 为了简单我们使用了全局变量，但是即使作为类变量（类中的static成员变量）也是一样的。这个方法就是使用&#8220;非局部的静态变量&#8221;来做成Mutex。</p>
<pre>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;方法f</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br></span><span style="color: #0000ff;">namespace</span><span style="color: #000000;">&nbsp;</span><span style="color: #008000;">/*</span><span style="color: #008000;">&nbsp;anonymous&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;">&nbsp;{<br>&nbsp;&nbsp;Mutex&nbsp;mutex;<br>}<br><br></span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;Foo::need_to_sync(</span><span style="color: #0000ff;">void</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;ScopedLock</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Mutex</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">lock</span><span style="color: #000000;">(mutex);<br><br>&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;"> 处理</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br>&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">;<br>}</span></div>
<br></pre>
<p>这个是最流行的方法，而且<span style="font-weight: bold;">基本</span>上可以没有问题就能工作得很好。</p>
<p>在一个全局的类对象x存在，且在x的构造函数中直接或者绕弯间接的调用Foo::need_to_sync函数的场合，会引起一些问题。也就是静态的对象的初始化顺序的问题，这个问题一般也被叫做"static initialization order fiasco" 。在执行到mutex的构造函数之前， mutex.Lock()有可能会被执行。</p>
<br>
<p><a href="http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12">这里的FAQ</a>的10.12～10.16<span class="footnote"><a href="http://d.hatena.ne.jp/yupo5656/20040713#20040713f5" title="和書 asin:489471194x に邦訳が載っています。また static
initialization order の問題については此処にもそのうち何か書きます" name="20040713fn5">*5</a></span>、在里面对自己的代码的初始化顺序已经证明了没有问题，而且将来也不会出现问题，所以上面的方法是OK的。</p>
<br>
<p>如果， 初始化顺序的问题不能保证他没有问题的话， 只好使用pthread_once的&#8220;方法d&#8221;，或者移植性低的&#8220;方法c&#8221;。我的个人感觉是方法c还是比较不错的选择。</p>
<br>
<p>在最后我们尝试考虑一下如何把方法c变成C++的代码。</p>
<pre>// 方法c (重新讨论)<br><br>void Foo::need_to_sync(void) {<br>  static pthread_mutex_t mutex = PTHREAD_MUTEX_RECURSIVE_INITIALIZER_NP;<br></pre>
<p>目标是、</p>
<ul>
    <li>隐藏pthread_mutex_t类型、让自己写的类的类型可见。
    </li>
    <li>在方法e,f中像使用ScopedLock模板那样进行修改。 </li>
</ul>
<p>当然，不让它发生初始化顺序的问题。<br></p>
<br></div>
<div class="section">
<h3><a href="http://d.hatena.ne.jp/yupo5656/20040713/p3" name="p3"><span class="sanchor">■</span></a>[<a class="sectioncategory" href="http://d.hatena.ne.jp/yupo5656/searchdiary?word=%2A%5BC%2B%2B%5D">C++</a>] 用C++编写synchronized method比较难 (3)<span class="hatena-star-comment-container"><img class="hatena-star-comment-button" title="Comments" style="border: medium none ; margin: 0pt; padding: 0pt; display: none; vertical-align: middle; cursor: pointer;" tabindex="0" alt="Comments" src="http://s.hatena.ne.jp/images/comment.gif"></span><span class="hatena-star-star-container"><img class="hatena-star-add-button" title="Add star" style="border: medium none ; margin: 0pt 3px; padding: 0pt; vertical-align: middle; cursor: pointer;" tabindex="0" alt="Add star" src="http://s.hatena.ne.jp/images/add.gif"></span></h3>
<p>这是方法c的改良。 首先， 为了避免发生初始化顺序的问题， 必须是不允许调用构造函数就能完成对象的初始化。因此，必须像下面那样初始化mutex对象</p>
<pre>// 方法c' (假设)<br><br>void Foo::need_to_sync(void) {<br>  static StaticMutex mutex = { PTHREAD_MUTEX_RECURSIVE_INITIALIZER_NP, ........ };<br></pre>
<p>一般的不允许像这样初始化C++类。为了实现上面那样的初始化，StaticMutex类必须是POD型的。所谓POD型就是， <br></p>
<ul>
    <li>不允许有构造函数
    </li>
    <li>不允许有析构函数
    </li>
    <li>不允许编译器生成拷贝构造函数， 赋值构造函数。
    </li>
    <li>不允许有private, protected 的成员
    </li>
    <li>不允许有虚函数 </li>
</ul>
<p>满足以上规格的类型<span class="footnote"><a href="http://d.hatena.ne.jp/yupo5656/20040713#20040713f6" title="詳しくは iso/iec 14882:2003 あるいは jis x 3014:2003
の「&#167;3.9/10 c互換型」「&#167;8.5.1/14 静的記憶期間をもつc互換型の集成体の波括弧で囲んだ初期化子並びによる静的な初期化」「&#167;9/4
c互換構造体」を参照のこと" name="20040713fn6">*6</a></span>。</p>
<br>
<p>大概有严格的制约,但是利用"定义非虚成员函数是没有问题的"这个特性, 我们尝试改良方法c的方案. <br></p>
<br>
<p>...像下面那样如何?</p>
<pre>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;方法c'</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br></span><span style="color: #0000ff;">#define</span><span style="color: #000000;">&nbsp;POD_MUTEX_MAGIC&nbsp;0xdeadbeef</span><span style="color: #000000;"><br></span><span style="color: #0000ff;">#define</span><span style="color: #000000;">&nbsp;STATIC_MUTEX_INITIALIZER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;PTHREAD_INITIALIZER,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;POD_MUTEX_MAGIC&nbsp;}</span><span style="color: #000000;"><br></span><span style="color: #0000ff;">#define</span><span style="color: #000000;">&nbsp;STATIC_RECURSIVE_MUTEX_INITIALIZER&nbsp;{&nbsp;PTHREAD_RECURSIVE_INITIALIZER_NP,&nbsp;POD_MUTEX_MAGIC&nbsp;}</span><span style="color: #000000;"><br><br></span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;PODMutex&nbsp;{<br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;Lock()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;assert(magic_&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;POD_MUTEX_MAGIC);<br>&nbsp;&nbsp;&nbsp;&nbsp;pthread_mutex_lock(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">mutex_);<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;Unlock()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;assert(magic_&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;POD_MUTEX_MAGIC);<br>&nbsp;&nbsp;&nbsp;&nbsp;pthread_mutex_unlock(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">mutex_);<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;typedef&nbsp;ScopedLock</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">PODMutex</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;ScopedLock;<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br>&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;虽然编程了POD型,&nbsp;但是不定义成public就是无效的</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;pthread_mutex_t&nbsp;mutex_;<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">const</span><span style="color: #000000;">&nbsp;unsigned&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;magic_;<br>};<br><br></span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;ScopedLock类模板是留用了在方法e,f中做成的代码.</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br></span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;Foo::need_to_sync(</span><span style="color: #0000ff;">void</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;PODMutex&nbsp;mutex&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;STATIC_RECURSIVE_MUTEX_INITIALIZER;<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;PODMutex::ScopedLock&nbsp;</span><span style="color: #0000ff;">lock</span><span style="color: #000000;">(mutex);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;处理.</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">;<br>}</span></div>
<br></pre>
<p>上面的代码满足了"隐藏了pthread_mutex_t型,留用了ScopedLock&lt;&gt;"这两个目的. 这不就是有点儿像C++的代码了吗? 还有,PODMutex类型是即使在上记例子中那样的局部静态变量以外,也能放心的使用全局变量,类变量了.</p>
<p>而且, 成员变量 magic_ 是, 一个const成员变量, 所以当使用编译器自动生成的构造函数来创建一个对象时就会发生错误. 因此,在构建release版程序时把它剔除就好了.</p>
<br>
<p>使用g++ -S来编译上面的代码, 生成汇编代码. 我们就能看见下面那样的局部的静态变量. <br></p>
<pre>$ g++ -S sample.cpp<br>$ c++filt &lt; sample.s | lv<br>(略)<br>        .size   Foo::need_to_sync()::mutex, 28<br>Foo::need_to_sync()::mutex:<br>        .long   0<br>        .long   0<br>        .long   0<br>        .long   1<br>        .long   0<br>        .long   0<br>        .long   -559038737<br></pre>
<p>0,0,0,1,0,0 这样的东西是 PTHREAD_RECURSIVE_INITIALIZER_NP , -559038737 则是 POD_MUTEX_MAGIC 。即使没有进行动态的初始化(不调用构造函数)、仅仅是在目标文件上生成的目标代码那样的进行静态初始化, mutex对象也能被正常的初始化, 所以这段代码是OK的.</p>
<br>
<p>随便, 在使用boost库的场合, 方法f之外的选择余地几乎没有(至少是现在). 一看见ML等, (当然!!)就知道可能会出现 order顺序的问题. 但是, 就目前来讲, 既要保证既要保证可移植性<a title="Windowsはどうする？という命題を解決できないのかもしれません - 推測" href="http://d.hatena.ne.jp/yupo5656/20040713#20040713f7" name="20040713fn7">*7</a>和速度,又要能做成与方法c相当的PODMutex的方法好像还没有出现吧.</p>
<br>
<p>完结</p>
</div>
<div class="footnote">
<p class="footnote"><a href="http://d.hatena.ne.jp/yupo5656/20040713#20040713fn1" name="20040713f1">*1</a>：non portable 的意思</p>
<p class="footnote"><a href="http://d.hatena.ne.jp/yupo5656/20040713#20040713fn2" name="20040713f2">*2</a>：递归mutex的例子的代码太冗长了,这里就省略 了. 根据pthread_mutex_init来进行初始化,就使得做成递归mutex变得比较容易了.</p>
<p class="footnote"><a href="http://d.hatena.ne.jp/yupo5656/20040713#20040713fn3" name="20040713f3">*3</a>：这里记载了 g++ -S的结果和解说</p>
<p class="footnote"><a href="http://d.hatena.ne.jp/yupo5656/20040713#20040713fn4" name="20040713f4">*4</a>：2005/12追记: 在最近的g++中发生异常、参照这里 <a href="http://d.hatena.ne.jp/yupo5656/20051215/p2" target="_blank">http://d.hatena.ne.jp/yupo5656/20051215/p2</a> <br></p>
<p class="footnote"><a href="http://d.hatena.ne.jp/yupo5656/20040713#20040713fn5" name="20040713f5">*5</a>：日语文献 <a href="http://d.hatena.ne.jp/asin/489471194X/casactdiary-22">ASIN:489471194X</a> 中记载着翻译版. 还有 static initialization order 的问题,在此处也有一些记载.</p>
<p class="footnote"><a href="http://d.hatena.ne.jp/yupo5656/20040713#20040713fn6" name="20040713f6">*6</a>：详细是参看 ISO/IEC 14882:2003 或者是 JIS X 3014:2003 的「&#167;3.9/10 C互換型」「&#167;8.5.1/14 静的記憶期間をもつC互換型の集成体の波括弧で囲んだ初期化子並びによる静的な初期化」「&#167;9/4 C互換構造体」这几个章节</p>
<p class="footnote"><a href="http://d.hatena.ne.jp/yupo5656/20040713#20040713fn7" name="20040713f7">*7</a>：Windows是如何做的? 可能不能解决这个命题 - 推测</p>
<p class="footnote"><br></p>
<p class="footnote">http://d.hatena.ne.jp/yupo5656/20051215/p2</p>
<p class="footnote">http://d.hatena.ne.jp/yupo5656/20041011#p1</p>
<p class="footnote">http://blogs.msdn.com/b/oldnewthing/archive/2004/03/08/85901.aspx</p>
<p class="footnote">http://d.hatena.ne.jp/yupo5656/20071008/p1<br></p>
</div><img src ="http://www.cppblog.com/lymons/aggbug/118502.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2010-07-17 12:17 <a href="http://www.cppblog.com/lymons/archive/2010/07/17/118502.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CodeBlocks插件开发指南(三)</title><link>http://www.cppblog.com/lymons/archive/2010/04/29/113991.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Thu, 29 Apr 2010 12:58:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2010/04/29/113991.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/113991.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2010/04/29/113991.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/113991.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/113991.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 1．	添加右键弹出菜单<br>我们在这里，将会在插件中加入右键弹出菜单功能。也就是如下图那样，在文档区内，点击鼠标右键时会弹出的菜单。我们将在这个菜单中加入属于我们插件的菜单项目”testplug”。<br> <br>加入菜单项目的效果如下图：<br> <br><br>让我们看看，咱们创建的插件工程中，给我们提供了什么样的入口代码了吧。<br>1. 在函数BuildModuleMenu中添加菜单项。<br>在文档的视图里才加入菜单项的入口函数是BuildModuleMenu,它的初始代码是下面那样：<br>void testplug::BuildModuleMenu(const ModuleType type, wxMenu* menu, const FileTreeData* data)<br>{<br>    //Some library module is ready to display a pop-up menu.<br>    //Check the parameter \"type\" and see which module it is<br>&nbsp;&nbsp;<a href='http://www.cppblog.com/lymons/archive/2010/04/29/113991.html'>阅读全文</a><img src ="http://www.cppblog.com/lymons/aggbug/113991.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2010-04-29 20:58 <a href="http://www.cppblog.com/lymons/archive/2010/04/29/113991.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CodeBlocks插件开发指南(二)</title><link>http://www.cppblog.com/lymons/archive/2010/04/20/113099.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Tue, 20 Apr 2010 14:21:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2010/04/20/113099.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/113099.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2010/04/20/113099.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/113099.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/113099.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 1． 创建Plugin工程<br><br>①       从File主菜单中，选择new -> Projects… , 打开工程创建向导对话框。<br>②       在Projects的列表框中，选择 Code::Blocks plugin ，然后点击 Go 按钮，进入插件创建向导对话框。<br>③       创建向导的第一个画面中有重要信息说明。请注意的是里面提到了必须设置#cb, #wx这两个全局变量。不过，还好咱们在《CodeBlock插件开发指南 一》中已经对这两个变量进行了设置。咱们在这里，直接点击Next进入到下一个画面即可。<br><br>&nbsp;&nbsp;<a href='http://www.cppblog.com/lymons/archive/2010/04/20/113099.html'>阅读全文</a><img src ="http://www.cppblog.com/lymons/aggbug/113099.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2010-04-20 22:21 <a href="http://www.cppblog.com/lymons/archive/2010/04/20/113099.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CodeBlocks插件开发指南(一)</title><link>http://www.cppblog.com/lymons/archive/2010/04/17/112850.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Sat, 17 Apr 2010 08:22:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2010/04/17/112850.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/112850.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2010/04/17/112850.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/112850.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/112850.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: CodeBlocks插件开发指南(一)<br>By Lymons(lymons@gmail.com)  2010/4/14<br><br>CodeBlocks是一个优秀的C/C++ IDE开发环境,另外它也是开源的,也是跨平台的.你能够利用它的源代码进行二次开发,也能为它制作自己的插件. 它的GUI是利用了跨平台的wxWidgets库来做成的,所以它才能够实现跨平台.<br><br>下面将介绍CodeBlock的插件制作方法,当然它必须得通过编写自己的程序才能实现,所以懂得一些C++的编程知识能够帮你更容易去理解它的内容.<br> <br>1． 准备CodeBlocks的开发环境&nbsp;&nbsp;<a href='http://www.cppblog.com/lymons/archive/2010/04/17/112850.html'>阅读全文</a><img src ="http://www.cppblog.com/lymons/aggbug/112850.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2010-04-17 16:22 <a href="http://www.cppblog.com/lymons/archive/2010/04/17/112850.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>W2K信号(Signals)的设备驱动</title><link>http://www.cppblog.com/lymons/archive/2010/04/13/112426.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Tue, 13 Apr 2010 02:29:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2010/04/13/112426.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/112426.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2010/04/13/112426.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/112426.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/112426.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: W2K信号(Signals)的设备驱动 <br><br>     Unix下的信号提供了一个简单的IPC机制，也就是当进程收到一个信号后会异步(asynchronous) 地调用你的信号处理函数(也叫做句柄)，不管你的代码是否已经处在执行的过程之中。 而在Windows 2000(译者注：版本高于W2k的Windows平台)下就需要用到一个设备驱动，以便你能使用异步过程调用(asynchronous procedure calls , 简称APCs或者APC) 来达成同样的效果.<br><br>By Panagiotis E. <br>August 01, 2001 <br>URL:http://www.ddj.com/windows/184416344 <br><br>翻译：Lymons (lymons@gmail.com)<br>&nbsp;&nbsp;<a href='http://www.cppblog.com/lymons/archive/2010/04/13/112426.html'>阅读全文</a><img src ="http://www.cppblog.com/lymons/aggbug/112426.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2010-04-13 10:29 <a href="http://www.cppblog.com/lymons/archive/2010/04/13/112426.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Exif文件格式描述</title><link>http://www.cppblog.com/lymons/archive/2010/02/23/108266.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Tue, 23 Feb 2010 01:53:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2010/02/23/108266.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/108266.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2010/02/23/108266.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/108266.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/108266.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Exif文件格式描述<br>当前，几乎新型的数码相机都使用Exif文件格式来存储图像. 它的规格是由 JEIDA 来制定的, 但是在互联网上还没有开放的文档可供浏览. 因此我根据从互联网上所能得到一些开放资料做成了这份Exif格式的描述文档.<br><br>注: 现在我们能得到官方的文档 Exif2.1 ，它来自 PIMA 的web站点.<br><br>ISO 正致力于建立 DCF (Design rule for Camera File system/相机文件系统设计规则) 规格. 所有的数码相机的制造商正准备遵循这份规则并且已经在他们的最新的数字相机上使用了. DCF规格为数字相机定义了完整的文件系统; 如，目录结构, 文件命名方法, 字符集和文件格式等等. 这里的文件格式就是基于 Exif2.1 规格制定的.<br>&nbsp;&nbsp;<a href='http://www.cppblog.com/lymons/archive/2010/02/23/108266.html'>阅读全文</a><img src ="http://www.cppblog.com/lymons/aggbug/108266.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2010-02-23 09:53 <a href="http://www.cppblog.com/lymons/archive/2010/02/23/108266.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用C++设计游戏引擎的框架</title><link>http://www.cppblog.com/lymons/archive/2009/12/22/103699.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Tue, 22 Dec 2009 06:57:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2009/12/22/103699.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/103699.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2009/12/22/103699.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/103699.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/103699.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 简介<br>我拿到了一个任务，就是写一篇关于游戏引擎设计的报告。为此，我开始用C++来实现一个框架，它包含了一些设计模式（Design Patterns）的基本实现以及类似于基于原则设计（Policy based design）的一些C++概念。而本文就是谈论我的设计，并且里面也包括一些可编译的代码片断。<br>背景<br>在本文描述的框架中使用了一些著名的设计范式（design paradigms），如：基于原则的设计（Policy based design），装饰者（Decorator）和策略（Strategy）模式，以及相应的C++的代码实现。<br>代码的功能说明<br>基于原则的设计是用于游戏的设置<br>在进入到足球游戏引擎设计的细节之前，先讨论一下游戏中的设置。在任何游戏中都允许用户在游戏开始期间来选择游戏的难度。我假设这里有三种难度级别，即：低级，中级，高级。因为这些级别允许在开始的时候被选择，这就给了我们一个机会可以利用模板类来使用基于原则的设计（基于Andrei Alexandrescu的书《Modern C++ Design》）。&nbsp;&nbsp;<a href='http://www.cppblog.com/lymons/archive/2009/12/22/103699.html'>阅读全文</a><img src ="http://www.cppblog.com/lymons/aggbug/103699.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2009-12-22 14:57 <a href="http://www.cppblog.com/lymons/archive/2009/12/22/103699.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在Visual Studio使用宏来定制新的注释格式</title><link>http://www.cppblog.com/lymons/archive/2009/12/21/103644.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Mon, 21 Dec 2009 13:13:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2009/12/21/103644.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/103644.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2009/12/21/103644.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/103644.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/103644.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 在本文中，使用Visual Studio2003作为例子来描述宏的做成步骤。（我想在Visual Studio２００５、２００８中也应该是一样的）	　<br>　	　	　<br>在编写ASP代码时，可以使用单引号来注释掉不要的代码。在Visual Studio的IDE环境中也支持使用块注释的方式把一整段的代码注释掉。	　<br>不过有的时候，也有要求用<%' ~　%>这种注释格式来注释掉HTML中的代码，但是包括Visual Studio在内的一些流行的编辑器都不支持这种格式的注释方式。不过还好，Visual Studio的IDE支持宏的功能，因此我们可以使用VBScript宏来定制任意格式的代码注释&nbsp;&nbsp;<a href='http://www.cppblog.com/lymons/archive/2009/12/21/103644.html'>阅读全文</a><img src ="http://www.cppblog.com/lymons/aggbug/103644.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2009-12-21 21:13 <a href="http://www.cppblog.com/lymons/archive/2009/12/21/103644.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>函数指针教程</title><link>http://www.cppblog.com/lymons/archive/2009/02/12/73604.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Thu, 12 Feb 2009 08:08:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2009/02/12/73604.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/73604.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2009/02/12/73604.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/73604.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/73604.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 函数指针提供了一些极其有趣，有效和绝妙的编程技术。你能用它代替switch/if语句来实现你自己的晚绑定（late-binding）或者作为回调（callback）来使用。不幸的是–可能由于它的语法比较复杂–几乎所有的电脑书籍和文档上都讲解的不多。即便如此，它们也只是做了相当简单和肤浅的说明。而对于函数指针你只需要明白它是什么以及它的语法，因为它和一般的指针比起来从来不用关心内存的分配和释放，所以它被使用的时候是不易产生错误的。但你要注意的是: 要时常问自己是否真的需要函数指针。因为虽然用它来实现晚绑定也很漂亮，但用既存的C++数据结构的话会使代码更可读和更简洁。另外，晚绑定的一方面实际上就是运行期（runtime）: 如果你调用了一个虚拟函数，你的程序会根据一个存储所有函数的虚拟表（V-Table）自己来确定到底真正调用的是哪一个。这就要花费一些时间而用函数指针代替虚拟函数的话有可能会节省一些时间。BTW: 现代的编译器在这方面都做得非常好！就那我的Borland编译器来说这个时间就比调用一次虚拟函数能节省2%。<br><br>注：晚捆绑(late binding)可能来自c++&nbsp;&nbsp;<a href='http://www.cppblog.com/lymons/archive/2009/02/12/73604.html'>阅读全文</a><img src ="http://www.cppblog.com/lymons/aggbug/73604.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2009-02-12 16:08 <a href="http://www.cppblog.com/lymons/archive/2009/02/12/73604.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>