??xml version="1.0" encoding="utf-8" standalone="yes"?>久久久久99精品成人片欧美,久久人人妻人人爽人人爽,国内高清久久久久久http://www.shnenglu.com/Solstice/zh-cnTue, 06 May 2025 14:35:04 GMTTue, 06 May 2025 14:35:04 GMT60?Boolan |开讌Ӏ网l编E实战》课E?/title><link>http://www.shnenglu.com/Solstice/archive/2014/12/03/209007.html</link><dc:creator>陈硕</dc:creator><author>陈硕</author><pubDate>Tue, 02 Dec 2014 17:50:00 GMT</pubDate><guid>http://www.shnenglu.com/Solstice/archive/2014/12/03/209007.html</guid><wfw:comment>http://www.shnenglu.com/Solstice/comments/209007.html</wfw:comment><comments>http://www.shnenglu.com/Solstice/archive/2014/12/03/209007.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.shnenglu.com/Solstice/comments/commentRss/209007.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/Solstice/services/trackbacks/209007.html</trackback:ping><description><![CDATA[<p>《网l编E实战》是一门以讲解实例Z的课E,每一节都讲一两个|络~程的例子程序,评偏重 Linux 服务?TCP |络~程?/p> <p>本课E要求听课h员已l读q《Unix |络~程》,能写单的 TCP echo 服务?/p> <p> </p> <p>评地址Q?a title="http://boolan.com/course/4" >http://boolan.com/course/4</a></p> <p>配套面Q?a >http://chenshuo.com/pnp</a></p><img src ="http://www.shnenglu.com/Solstice/aggbug/209007.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/Solstice/" target="_blank">陈硕</a> 2014-12-03 01:50 <a href="http://www.shnenglu.com/Solstice/archive/2014/12/03/209007.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《Linux 多线E服务端~程Q?muduo C++ |络库》电子版上市http://www.shnenglu.com/Solstice/archive/2013/11/01/204039.html陈硕陈硕Fri, 01 Nov 2013 02:35:00 GMThttp://www.shnenglu.com/Solstice/archive/2013/11/01/204039.htmlhttp://www.shnenglu.com/Solstice/comments/204039.htmlhttp://www.shnenglu.com/Solstice/archive/2013/11/01/204039.html#Feedback1http://www.shnenglu.com/Solstice/comments/commentRss/204039.htmlhttp://www.shnenglu.com/Solstice/services/trackbacks/204039.html《Linux 多线E服务端~程Q?muduo C++ |络库?电子版已在京东上市销售?/p>

购买地址Q?a >http://e.jd.com/30149978.html

阅读效果Q?/p>

PC

ebookpc

iPad

ebookipad

目前京东的阅d没有切白边功能,值得改进?/p>

陈硕 2013-11-01 10:35 发表评论
]]>
C++面试中stringcȝ一U正简明的写法http://www.shnenglu.com/Solstice/archive/2013/10/11/203654.html陈硕陈硕Fri, 11 Oct 2013 02:59:00 GMThttp://www.shnenglu.com/Solstice/archive/2013/10/11/203654.htmlhttp://www.shnenglu.com/Solstice/comments/203654.htmlhttp://www.shnenglu.com/Solstice/archive/2013/10/11/203654.html#Feedback0http://www.shnenglu.com/Solstice/comments/commentRss/203654.htmlhttp://www.shnenglu.com/Solstice/services/trackbacks/203654.html本文首发于酷壳网 http://coolshell.cn/articles/10478.html

先说说程序员Q应届生Q面试的一般过E,一轮面试(面对一C个面试官Q一般是四、五十分钟,面试官会问两三个~程问题Q通常是两大一)Q因此留l每个编E题的时间只?20 分钟。这 20 分钟不光是写代码Q还要跟面试官讨Z的答案ƈ解答提问Q比如面试官拿过你的{案U,问某一行代码如果修改会有什么后果。因此真正留l在U怸或白板上写代码的旉也就 10 分钟上下。本文给Z一个能?10 分钟旉在纸上写出来且不会有错的 String classQ强调正性及易实玎ͼ白板上写也不会错Q,不强调效率与功能完备?/p>

本文的配套代码位?https://github.com/chenshuo/recipes/blob/master/string/StringTrivial.h?/p>

全文Q?a title="https://chenshuo.googlecode.com/files/CppEngineering.pdf" >https://chenshuo.googlecode.com/files/CppEngineering.pdf



陈硕 2013-10-11 10:59 发表评论
]]>
读者来信与解答 1http://www.shnenglu.com/Solstice/archive/2013/10/10/203638.html陈硕陈硕Thu, 10 Oct 2013 04:17:00 GMThttp://www.shnenglu.com/Solstice/archive/2013/10/10/203638.htmlhttp://www.shnenglu.com/Solstice/comments/203638.htmlhttp://www.shnenglu.com/Solstice/archive/2013/10/10/203638.html#Feedback0http://www.shnenglu.com/Solstice/comments/commentRss/203638.htmlhttp://www.shnenglu.com/Solstice/services/trackbacks/203638.html读者来信用黑色Q我的回{用蓝色。经q整理,接近对话体?/p>

> 陈硕Q你好,
>
> 阅读了你的书Q很有收莗?br>> 但是没有在moduo的源代码里面扑ֈ实现U程模型11的例子。即one thread per loop + thread pool?br>> 谢谢?/p>

书第 173 图 6-14 下面的第一D话Q具体改动方法参考前一늚 diff?/font>


> 谢谢?br>>
> 另外TcpConnection和Channel的生命周期管理有炚w题?br>> TcpConnection如果已经被回收了Q其包含的Channel也已l被回收了。而这个时候在Channel::handleEvent()里面查tied_和tie_是危险的。因为其内存已经被回收了?br>>
> 如果用户保证TcpConnection被回收之后,不会再用Channel的裸指针Q则没有必要在TcpConnection::connectEstablished()中call tie().

TcpConnection 回收之前Q会调用 connectDestroyedQ其中调?channel_->remove();Q这样就不可能再会有 Channel::handleEvent() 被调用了?/font>

tie() 的作用是防止 Channel::handleEvent() q行期间?owner 对象析构Q导?Channel 本n被销毁?/font>


> > TcpConnection 回收之前Q会调用 connectDestroyedQ其中调?channel_->remove();Q这样就不可能再会有 Channel::handleEvent() 被调用了?br>> q个时候会不会有race conditionQ假讄在有两个active channelsQ处理头一个的时候回收TcpConnectionQ而第二个channel刚好对应q个connection?/p>

q时你没有办法强刉?TcpConnectionQ只能降低其引用计数Q所以不会有问题。你可以写段代码试试?/font>

> 另外底层的poller OS api是否保证unregister channel之后一定不会再有这个channel的事Ӟ会清I内核的已经qA的事仉列?

跟内核没关系QPoller class ?unregister channel 之后׃可能调用?handleEvent() 成员函数?/font>

> 那EPollPoller::fillActiveChannels()的改一改,“assert(it != channels_.end());”不再适用了,而且每次都个event都要查一ơmap。效率会有问题?/p>

assert() 只有?debug build 才执行,不会影响效率?br>再说每个 event 都要涉及 read/write {系l调用,开销比“查一?map”大得多Q优化这里是无用功?/strong>

> 但这个assert()不是invalid了吗Q你可能之前在unregister channel的时候已l从map里面remove掉了它?

q个 assert 是有效的Q你再想惟?/font>


> > tie() 的作用是防止 Channel::handleEvent() q行期间?owner 对象析构Q导?Channel 本n被销毁?/font>

> q个也不太make sense。仍然有race conditon。在Channel::handleEvent()拥有guard锁定ownner之前QChannel::handleEventQ?需要检查其tied_?/p>

你再xQtie 的作用是防止调用 handleEvent() 期间对象销毁(比如调用 closeCallback 期间Q,不是也不可能防止调用 handleEvent() 之前对象销毁?/strong>

> 恩,是的。整个TcpConnection, Channel, EventLoop都是一个thread里面run的?/font>



陈硕 2013-10-10 12:17 发表评论
]]>
[转蝲]赖勇:推荐《Linux 多线E服务器端编E?/title><link>http://www.shnenglu.com/Solstice/archive/2013/10/06/202822.html</link><dc:creator>陈硕</dc:creator><author>陈硕</author><pubDate>Sat, 05 Oct 2013 18:15:00 GMT</pubDate><guid>http://www.shnenglu.com/Solstice/archive/2013/10/06/202822.html</guid><wfw:comment>http://www.shnenglu.com/Solstice/comments/202822.html</wfw:comment><comments>http://www.shnenglu.com/Solstice/archive/2013/10/06/202822.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/Solstice/comments/commentRss/202822.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/Solstice/services/trackbacks/202822.html</trackback:ping><description><![CDATA[<p style="margin-top: 10px; margin-bottom: 10px; font-family: verdana, Arial, Helvetica, sans-serif;">推荐《Linux 多线E服务器端编E?nbsp;</p><p style="margin-top: 10px; margin-bottom: 10px; font-family: verdana, Arial, Helvetica, sans-serif;">赖勇(http://laiyonghao.comQ?/p><p style="margin-top: 10px; margin-bottom: 10px; font-family: verdana, Arial, Helvetica, sans-serif;">最q,有一位朋友因为工作需要,需要从|游的客L~程转向服务器端~程Q找我推荐一本书。我推荐了《Linux 多线E服务器端编E?#8212;—使用 muduo C++ |络库》给他,他在|上书店看了以后问我Z么推荐这么厚一本书l他Q正好这本书我已l早q完了Q一直也惛_?#8220;书评”Q就在这里多扯几句。其实实在算不上书评Q原因有二:一是读书的时候囫囵吞枣,理解不够深刻Q不深刻自然不能评;二是q几q虽然在 Linux 下写服务器端的网l程序,但很用多线E,也很用 C++Q书里谈的东西,是不熟(zhn)的领域Q自然也不能p。所以今天这,应当是推荐,是ؓ陈硕老师背书?/p><p style="margin-top: 10px; margin-bottom: 10px; font-family: verdana, Arial, Helvetica, sans-serif;">l箋阅读Q?/p><p style="margin-top: 10px; margin-bottom: 10px; font-family: verdana, Arial, Helvetica, sans-serif;"><a target="_blank" data-mce->http://blog.csdn.net/gzlaiyonghao/article/details/10366863</a></p><p style="margin-top: 10px; margin-bottom: 10px; font-family: verdana, Arial, Helvetica, sans-serif;"><a target="_blank" data-mce->http://book.douban.com/review/6249351/</a></p><img src ="http://www.shnenglu.com/Solstice/aggbug/202822.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/Solstice/" target="_blank">陈硕</a> 2013-10-06 02:15 <a href="http://www.shnenglu.com/Solstice/archive/2013/10/06/202822.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用条件变量实C件等待器的正与错误做法http://www.shnenglu.com/Solstice/archive/2013/09/09/203094.html陈硕陈硕Sun, 08 Sep 2013 19:01:00 GMThttp://www.shnenglu.com/Solstice/archive/2013/09/09/203094.htmlhttp://www.shnenglu.com/Solstice/comments/203094.htmlhttp://www.shnenglu.com/Solstice/archive/2013/09/09/203094.html#Feedback21http://www.shnenglu.com/Solstice/comments/commentRss/203094.htmlhttp://www.shnenglu.com/Solstice/services/trackbacks/203094.htmlTL;DR 如果你能一眼看?https://gist.github.com/chenshuo/6430925 中的?8 ?Waiter classes 哪些是对的哪些是错的Q本文就不必看了?/p>

前几天,我发了一条微?http://weibo.com/1701018393/A7FrW7ZVd Q质疑某本书?Pthreads 条g变量的封装是错的Q因为它没有?mutex ?lock()/unlock() 函数暴露出来Q导致无法实用。后来大家讨论的分歧是这?cond class 是不是通用的条件变量封装,q是只是一个特D的“事g{待?#8221;。作Z件等待器Q其实现也是错的Q因为存在丢׃件的可能Q可以算是初学者用条件变量的典型错误?/p>

本文的代码位?recipes/thread/test/Waiter_test.ccQ这里提到的某书的版本相当于 Waiter1 class?/p>

我在拙作《Linux 多线E服务端~程Q?muduo C++ |络库》第 2.2 节ȝ了条件变量的使用要点Q?/p>

条g变量只有一U正用的方式Q几乎不可能用错。对?wait 端:
1. 必须?mutex 一起用,该布?yu)表辑ּ的读写需受此 mutex 保护?br />2. ?mutex 已上锁的时候才能调?wait()?br />3. 把判断布?yu)条件?wait() 攑ֈ while 循环中?/font>

对于 signal/broadcast 端:
1. 不一定要?mutex 已上锁的情况下调?signal Q理ZQ?br />2. ?signal 之前一般要修改布尔表达式?br />3. 修改布尔表达式通常要用 mutex 保护Q至用?full memory barrierQ?br />4. 注意区分 signal ?broadcastQ?#8220;broadcast 通常用于表明状态变化,signal 通常用于表示资源可用。(broadcast should generally be used to indicate state change rather than resource availability。)”

如果用条件变量来实现一?#8220;事g{待?Waiter”Q正的做法是怎样的?我的最l答案见 WaiterInMuduo class?#8220;事g{待?#8221;的一U用途是E序启动时等待初始化完成Q也可以直接?muduo::CountDownLatch 到达相同的目的,初D?1 卛_?/p>

以下Ҏ(gu)微博上的讨论q程l出几个正确或错误的版本Q博大家一W?font color="#0000ff">只要C Pthread 的条件变量是Ҏ(gu)触发Qedge triggerQ,?signal()/broadcast() 只会唤醒已经{在 wait() 上的U程(s)Q?/font>我们在编码时必须要考虑 signal() 早于 wait() 的可能,那么很Ҏ(gu)判断以下各个版本的正误了。代码见 recipes/thread/test/Waiter_test.cc?/p>

版本一Q?font color="#ff0000">错误。某书上的原始版Q有丢失事g的可能?/p>

1

版本二:错误。lock() 之后?signal()Q同h丢失事g的可能?/p>

2

版本三:错误。引入了 bool signaled_; 条gQ但没有正确处理 spurious wakeup?/p>

版本四五六:正确。仅?single waiter 使用?br />

版本七:最佟뀂可?multiple waiters 使用?br />

版本八:错误。存?data raceQ且有丢׃件的可能。理p http://stackoverflow.com/questions/4544234/calling-pthread-cond-signal-without-locking-mutex

ȝQ用条件变量,调用 signal() 的时候无法知道是否已l有U程{待?wait() 上。因此一般L要先修改“条g”Q其ؓ trueQ再调用 signal()Q这?wait U程先检?#8220;条g”Q只有当条g不成立时才去 wait()Q避免了丢事件的可能?span style="color: #333333; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; background-color: #ffffff;">换言之,通过使用“条g”Q将Ҏ(gu)触发Qedge triggerQ改为电q发(level triggerQ?/span>q里“修改条g”?#8220;查条?#8221;都必d mutex 保护下进行,而且q个 mutex 必须用于配合 wait()?/p>

思考题Q如果用两个 mutexQ一个用于保?#8220;条g”Q另一个专门用于和 cond 配合 wait()Q会出现什么情况?

最后注明一点,http://stackoverflow.com/questions/6419117/signal-and-unlock-order q篇帖子里对 spurious wakeup 的解释是错的Qspurious wakeup 指的是一?signal() 调用唤醒两个或以?wait()ing 的线E,或者没有调?signal() 却有U程?wait() q回。manpage 里对 Pthreads pd函数的介l非常到位,值得l读?/p>

陈硕 2013-09-09 03:01 发表评论
]]>
用muduo实现memcached协议的例?/title><link>http://www.shnenglu.com/Solstice/archive/2013/08/18/202613.html</link><dc:creator>陈硕</dc:creator><author>陈硕</author><pubDate>Sun, 18 Aug 2013 04:59:00 GMT</pubDate><guid>http://www.shnenglu.com/Solstice/archive/2013/08/18/202613.html</guid><wfw:comment>http://www.shnenglu.com/Solstice/comments/202613.html</wfw:comment><comments>http://www.shnenglu.com/Solstice/archive/2013/08/18/202613.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/Solstice/comments/commentRss/202613.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/Solstice/services/trackbacks/202613.html</trackback:ping><description><![CDATA[<p>最q花了两天时间用 muduo 部分实现?memcached 服务器协议,代码位于 examples/memcached/serverQ能通过 memcached 的大部分试用例Qincr/decr q没有实玎ͼ?/p> <p>q不?memcached 的替代品Q它没有实现LRU和超时功能,也没有实Cq制协议Q更没有自己理内存Q,而是一个网l编E的CZQ代码只?1000 行,?memcached 很多)Q展C?muduo 风格的事仉动编E,以及来性能优化的试验品Q换句话_现在q个版本完全没有在性能上做ZQ何努力)。读q?memcached 代码的h可以Ҏ(gu)q两U编E风格的区别Qmemcached ?read/write 操作I插于正帔R辑处理Q?muduo 的网l数据读写是由库完成Q应用程序只兛_消息收发Q目前二者的基本 get/set 操作的性能相当?/p> <p>现在 muduo ?inspector 内置?gperftools 的远E?profiling 功能Qmemcached-debug 展示了其用法?/p> <p>Z么不必优?set 操作Q含 set/add/update/append/prepend/cas {)的性能Q?/p> <p>1. 比例。既然是 memcacheQ那?get:set 的比例很高,10:1 甚至更高Q因此优化的重心应该?get 而非 set?/p> <p>假设 memcached 能处?100k QPSQ再假设q些操作都是 setQ其实应该不?10% ?setQ,再假设所有的 set 都是串行执行的(没有q发Q,那么每次 set ?CPU 旉不应该超q?10 usQ含服务器本地的|络代码q行旉Q但不含|络延迟Q。而实际上一?set ?CPU 旉最多是 2~3 us Q用 memcached-footprint E序得Q,Ҏ(gu)不值得优化?/p> <p>2. |络带宽。假设一?set 操作?key + value 的长度是 1k bytesQTCP 的有效蝲荷带宽按110MB/s估算Q那?kB数据在千兆网上的惯性gq是 9usQ传输gq是几十上百微秒Q与此无养IQ也是说服务器的网卡收到这 1kB 数据需要花 9us 旉Q从W一个字节到辑ֈ服务器到收完最后一个字节)Q那么在 set 耗时 2~3 us 的情况下再去优化它是做无用功?/p> <p>3. 产生“需要更新的数据”的成本远大于 memcached set 的开销。memcached 需要更斎ͼ往往是将已写入数据库的新数据攑ֈ memcached 中,那么写数据库的开销q远大于 memcached set 的开销Q优?set Ҏ(gu)升系l整体性能没意义?/p><img src ="http://www.shnenglu.com/Solstice/aggbug/202613.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/Solstice/" target="_blank">陈硕</a> 2013-08-18 12:59 <a href="http://www.shnenglu.com/Solstice/archive/2013/08/18/202613.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>q期微博吐槽a论存档,涉及&ldquo;性能优化&rdquo;、C++陋习{?/title><link>http://www.shnenglu.com/Solstice/archive/2013/08/12/202489.html</link><dc:creator>陈硕</dc:creator><author>陈硕</author><pubDate>Mon, 12 Aug 2013 05:54:00 GMT</pubDate><guid>http://www.shnenglu.com/Solstice/archive/2013/08/12/202489.html</guid><wfw:comment>http://www.shnenglu.com/Solstice/comments/202489.html</wfw:comment><comments>http://www.shnenglu.com/Solstice/archive/2013/08/12/202489.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.shnenglu.com/Solstice/comments/commentRss/202489.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/Solstice/services/trackbacks/202489.html</trackback:ping><description><![CDATA[<p><font color="#0000ff">写C++E序的几个陋习:class 名以大写 C 开_例如 CDateQ成员变量以 m_ 开_变量采用匈牙利命名法Q不知道何时用 copy-ctor/assign operator?/font>前三个可能是从MFC那里传下来的Q当时C++、class、OO是新玩意儿,要与 C struct 区分Q现在还q么做就土了。C++的成员变量可用特D命名格式,加下划线后缀卛_Q加下划U前~是错的)。但?Java 里不必模?C++ 的这U成员变量命名方式,IDE 可以让成员变量以不同的颜色显C,与局部变量区分,Ҏ(gu)无需Ҏ(gu)命名。写E序怕把以前的编E经验不加区分地应用到新语言中,写成四不像,不地道?/p> <p>知道用 copy-ctor/assign operator ?C++ E序员的试金矟뀂在看到一个开源项目时Q我一般会先查看其 RAII handle class 是否用?copy-ctor/assign operatorQ例?Thread、Mutex、CondVar、ConnectionQ,如果没有Q对其第一印象很差了?/p> <p>关于 class 命名风格QGoogle、LLVM、Mozilla、muduo 都采?Pascal 风格QLikeThisQ,例如 EventLoop、SudokuSolver {等。正巧它们也都是?2 格羃q的Q可以用 clang-format 自动格式化代码?/p> <p>Z说说<font color="#0000ff">我不认同的两?C++ 教条Q?. 用nullptr替换NULLQ?. 用cstdio头文件替换stdio.h?/font></p> <p>因ؓ例如 gettimeofday(&tv, NULL) q种pȝ函数传个 nullptr q去实在是违和,现在?NULL 也能辑ֈ nullptr 的好处,大不了在某个头文仉define一下就行。这条将来或怼变?/p> <p>另外 ctime 头文件没定义 std::gmtime_rQ?time.h 定义?::gmtime_r。我可不惛_背哪些函数是 C 语言的哪些是 Posix 的,哪些头文件是 C 语言的哪些是 Posix 的(在Linux下,二者基本不分家Q。ؓ了用几个pȝ函数Q例?fcntl() Q,我该 include cfcntl q是 fcntl.hQ用U程?cpthread q是 pthread.hQ我LC?memset() 的参数顺序,因此一般用 bzero() 代替Q但?manpage ?bzero() 声明?strings.hQ那我要不要考虑试试 cstrings 呢?何必l自己找ȝQC++ 标准库之外的内容q脆l一?.h 头文件好了?/p> <p><font color="#0000ff">性能优化Q?/font></p> <p>有些人常常把“性能”挂在嘴边Q而且其以“提高性能”为理q“优化措施”往往不到点子上,只增加了复杂性和l护隑ֺQ降低了代码质量。这属于决策Ҏ(gu)偏了。我发现初学者往往q分x微观Q语句Q性能Q比方说兛_ while(true) ?for(;;) 哪个更快Q?+i ?i=i+1 哪个更快Qi/=16 ?i >>= 4 哪个快等{,而忽视了C~译器的优化能力?/p> <p>有的性能优化Q一是拿不出具体的合理的性能目标Q只惌快越好,二是不能实际准确量验证性能数据Q凭感觉和过时经验行事。在~码的时候,遇到两种做法都可行,决策办法是凭感觉猜?#8220;性能会更?#8221;的一U,而忽视了其他更重要的因素。可L和性能的典型关pd下图Q有多少场合是值得Z性能而牺牲代码的可读性和可维护性呢Q我希望自己的代码位于第 3 区,而一些h以ؓ自己的代码是在第 4 区,其实是在W?1 区?/p> <p><img title="read-perf" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="read-perf" src="http://www.shnenglu.com/images/cppblog_com/Solstice/Windows-Live-Writer/C_1284B/read-perf_707d60fb-8d33-4cfd-b867-94f3d946d2f5.png" width="470" height="329" /></p> <p>能在W?4 区写代码的h属于凤毛麟角Q有时候你费劲优化了半天,l果新CPU加了几条指oQ直接在g层面把问题解决了。现在一些h动不动就要挽赯子自己写内存池,L能提高性能Q真?Ulrich Drepper 是水货?Q书W?12.2.8 ?#8220;有必要自行定制内存分配器?#8221;Q你打算如何试内存分配器(mallocQ的性能Q有哪些指标Q有哪些影响因素需要控制或模拟Q比如线E数Q?你的试l果是否反映实际场景Q?/p> <p><font color="#0000ff">杂项</font></p> <p>有h问ؓ什么我?#8220;poco不是服务端C++|络?#8221;Q?<a >http://www.oschina.net/question/12_120943</a> Q,虽然它也提供了reactorQ因为它的reactor用的?Socket::select()Q虽然后者包装了epollQ但看其实现q道,它每ơ调用都会创建ƈ销?epoll fdQ然后重建整个watch listQ没有哪个服务端|络库会q么做?br /></p><div>嗯,世界上有两种|络~程Q网l编E和Windows|络~程?/div><img src ="http://www.shnenglu.com/Solstice/aggbug/202489.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/Solstice/" target="_blank">陈硕</a> 2013-08-12 13:54 <a href="http://www.shnenglu.com/Solstice/archive/2013/08/12/202489.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《Linux多线E服务端~程Q用muduo C++|络库》上市半q重Cơ,d数达C9000?/title><link>http://www.shnenglu.com/Solstice/archive/2013/07/17/201891.html</link><dc:creator>陈硕</dc:creator><author>陈硕</author><pubDate>Wed, 17 Jul 2013 03:17:00 GMT</pubDate><guid>http://www.shnenglu.com/Solstice/archive/2013/07/17/201891.html</guid><wfw:comment>http://www.shnenglu.com/Solstice/comments/201891.html</wfw:comment><comments>http://www.shnenglu.com/Solstice/archive/2013/07/17/201891.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.shnenglu.com/Solstice/comments/commentRss/201891.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/Solstice/services/trackbacks/201891.html</trackback:ping><description><![CDATA[<p>《Linux多线E服务端~程Q用muduo C++|络库》这本书自今q一月上市以来,半年之内已经重印两次Q加上首华ͼ一共是三次印刷Q,d数达C9000册,q在技术书里已l算是相当不错的成W。本书购买方式见配套|站 <a >http://chenshuo.com/book</a> ? </p><p>以下谈一谈这本书的写作背景与内容取舍的原因? </p><p>参加工作以来Q我~写q维护了若干C++/Java多线E网l服务程序,q本书ȝ了我在开发维护这cL务程序方面的l验。工作中Q我没有写过单线E的|络服务E序Q没有写qC语言的网l服务程序,也没有写q运行在Windows下的|络服务E序Q因此本书不涉及q些内容? </p><p>?#8220;Linux服务端开?#8221;q一背景下,q本书主要讲三个斚w的内?a href="#_ftn1_3988" name="_ftnref1_3988">[1]</a>Q现代C++、多U程、网l编E,分别对应书的W???部分。这不是一本入门书Q本书的读者应该在以上三方面已l具备相当的基础<a href="#_ftn2_3988" name="_ftnref2_3988">[2]</a>Q网l编E方面,能轻松读?.1节的两个PythonE序QC++斚wQ对12.8节的代码不感到陌生;多线E方面,能明白第1章要解决什么问题? </p><p>W??#8220;分布式系l工E实?#8221;详细介绍了这本书的应用背景,卛_发公司内部的分布式服务系l,书中的很多决{(design decisionQ和技术取舍(trade-offQ是在这一应用场景下做出的。以下是各章直接的交叉引用关pdQ没有计引用次敎ͼQ其中第0章是前言Q字母章节是附录。可见第9章是被引用最多的一章,某种意义上可以说W?章既是本书的先决条gQ又是本书的l极目标。由于章节之间存在众多的交叉引用Q去掉Q何一章都会破坏内容的完整性? </p><p><img title="ref" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" alt="ref" src="http://www.shnenglu.com/images/cppblog_com/Solstice/WindowsLiveWriter/LinuxmuduoC_112D7/ref_3.png" border="0" height="782" width="819" /> </p><p>q本书的书名原本打算?#8220;Linux C++ 多线E系l编E?#8221;。写完之后发玎ͼ与其他Unix/Linuxpȝ~程斚w的书不同Q这本书有明的应用场景Q因此可以砍掉很多内容,H出重点。甚臛_以说我主要讲别的书没有讲到的内容。这不是一本面面俱到的书,因此最l的书名也就不叫“pȝ~程”了? </p><p>同时Q我认ؓ很多教科书上介绍的一些做法是q时的(signalQ,一些是不推荐用的Q从外部l止U程、TCP OOB数据Q,一些是大多数情况下没必要用的Q内存池、lock-free ~程Q。作为全面的教材和手册,把这些内Ҏ(gu)q去可以理解。但是这本书定位是经验ȝQ我略去了教U书上那些基本用不到的知识点Q以免喧宑֤主,也徏议读者不要把_֊花在那些ơ要问题上? </p><ul> <li>q本书没有花很大的篇q去讲signalQ而是在第4.10节说明多U程E序不要使用signal作ؓIPC。ƈ且,在muduo-protorpc的示例中l出了Linux专有的signalfd(2)的用法,可以避免传统signal handler的常见陷阱,也更W合UNIX?#8220;everything is a file”哲学。第4.4节说明不要从外部l止U程Q因此也׃必去l究Pthreads cancellation point了。多U程E序最好不要fork()Q第4.9节)? </li><li>q本书没介绍daemonq程Q我认ؓdaemon是过时的做法。因为daemonq程的父q程是init(1)Q配|文件在本机Q不便于多机l一监控与管理(W?.8节)。(注:<span id="docs-internal-guid-2b7fe252-edc3-72f2-70b1-66266a658e79" style="font-size: 15px; font-family: Arial; color: #000000; background-color: transparent; font-weight: normal; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline;" data-mce-style="font-size: 15px; font-family: Arial; color: #000000; background-color: transparent; font-weight: normal; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline;">如果是第三方标准的服务程序,又不需要经常升U或攚w|重启,q且一旦崩溃,重启pl箋服务Q那么做?daemon 让init(1)接管是可以的Q比如ntpd、sshd{。这里谈的是自己开发维护的服务E序?/span>Q另外,Java/Python/Go写的服务E序g也没有做成daemon的习惯,C++E序没有理由要特D对待。补充一点,Linux的进E管理机制很落后Q从UNIXl承而来Q,子进E退出的事g只能被父q程以SIGCHLD信号的方式收刎ͼ而且q个signal可能丢失Q,kill(pid) 也存在很多race conditionQ你怎么保证pid在kill之前的一瞬间q代表你想kill的那个进E,而不是一个新启动的进E?close(fd)׃会有q种 race condition。)。这些困隑֜用户态无法克服,只能修改内核Q引入新的系l调用才能治本。例?FreeBSD 9.0 引入?pdfork()/pdkill() {,子q程变成文g描述W,q样p用IO事g框架l一处理了,也符合UNIX?#8220;everything is a file”哲学。但愿Linux内核也能快引入cM的系l调用,减轻E序员的负担? </li><li>q本书没有讲内存池,而是说明不是每个E序都要自己写内存池Q?#167;12.2.8Q。这本书也没有把“避免内存片”挂在嘴边Q而是Z么一般的E序不必在意它(§A.1.8Q; </li><li>q本书只xLinuxQ不考虑UL性。它推荐使用Linux专有的gettid()pȝ调用作ؓU程标识Q第4.3节)Q而不是用pthread_self()? </li><li>q本书不讲POSIX中五花八门的定时函数Q而专讲用LinuxҎ(gu)的timerfd来实现高_ֺ定时Q?#167;7.8.2Q,因ؓ它能方便地融入IO事g处理框架。muduo直接使用C++标准库来理定时器,而不是自己实现小堆(heap)Q这样可以简化实玎ͼ§8.2.1Q? </li><li>q本书只讲mutex和condition variable作ؓ最基础的线E同步手D(W?章)Qƈ且我认ؓ一个C++多线E程序代码里不应该直接出现pthread_mutex_lock之类的基本Pthreads调用。本书进一步徏议只使用非递归的mutexQ?#167;2.1.1Q,q与某些|上文章的推荐正好相反。这本书W?.3节甚臛_议不要用读写锁和信号量QsemaphoreQ,因ؓ一是容易用错,二是不见得能提高性能。mutex和condition variable是完备的Q能实现多种更易用的同步设施Q例如CountDownLatch和BlockingQueue?#167;12.8.3的代码展CZ用BlockingQueue和ThreadPool控制q发度的手法Q做C“No locks. No condition variables. No callbacks.” </li><li>q本书不讲lock-free~程Q因为编写可靠的lock-free代码q分析验证其正确性的隑ֺq大于编写普通的使用mutex和condition variable的多U程代码Q后者已l有了相当成熟的理论和工兗我认ؓlock-free不是每个多线E程序员应该掌握的技术,它投入高而用处少Q可以适当了解Q但不值得每个人都LI。只需要少Ch用它实现装好的数据l构Q像我这L普通h可以受益? </li><li>q本书只讲BSD Sockets作ؓq程间通信的手D,q且只用TCP长连接(§3.4Q。这样就砍掉了pipe、FIFO、POSIX message queue、shared memory、STREAMS、UNIX domain socket{等内容Q因为它们都只限本机q程间通信Q无法扩展到多机? </li><li>|络~程斚wQ第6?章)Q这本书不讲Sockets API的基本用法,而且代码中也不会直接使用它们。我认ؓ在程序中直接使用 Sockets API是初学者的做法Q当写一个新|络服务E序Q如果一开始考虑的是怎么l织accept、read、epoll_wait{调用,q种做法无异于用铅笔刀锯大树,事倍功半,也不利于来的功能扩展和l护。稍微像L的公叔R会用成熟的网l库Q不一定开源)Q把|络~程的复杂性封装进去,暴露好易用的接口Q让开发h员用更高层的building blocksQ消息传递或RPCQ从功能的角度去设计E序Q避免一ơ次反复掉到TCP|络~程的坑里。多个服务程序共享相同的基础库和事g处理框架的益处是显而易见的Q一斚w把网l编E的复杂性集中到一P避免每个团队都去t一遍坑Q另一斚wQ基库的bug修复与性能优化能惠及用到它的全部服务程序;最后,E序l构上的怼性让~程l验更加通用Q多个服务程序在功能、性能、正性等斚wh共性,能D一反三触类旁通,降低来开发维护的成本。应该避免每个程序都另v炉灶Q单独设计其IO事g处理l构? </li><li>q本书只讲非dIOl合IO复用QIO-MultiplexingQ这一Uƈ发风|归纳Z个半事gQ,q介l在多线E下的扩展(one loop per threadQ。IO复用斚wQ本书只讲level-triggerQ不讲edge-trigger。一斚w目前没有up to date的测试表明ET更快Q相反,我认为LT在读取数据时可以节约一ơread()调用Q?#167;8.7.2Q;另一斚wQLT模式更容易与其他W三方库l合Q?#167;7.15Q。多U程E序理q发socket fd有很多风格可供选择Q例如epoll fd是多个线E共享一个(多对一Q还是每个线E有自己的epoll fdQ一对一Q,每个socket fd是只属于一个epoll fdQ多对一Q还是可以同时属于多?epoll fdQ多对多Q,每个socket fd是只能被固定的一个线E读写还是可以被多个U程dQ如果是后者,那么d的时候是加锁q是使用ONESHOTQ。以上不是每U都可行Q本书也没有一一加以分析Q而是使用one loop per threadq种适用性较强的风格Q首先是正确性容易验证,其次是性能也能满要求? </li><li>本书不讲IPv6Q因为目前世界上最大的公司的服务机也用不完一个私有Acd址Q?0.0.0.0/8Q。本书不讲UDPQ因为《Unix|络~程》已l讲得很好了? </li><li>q本书D的网l编E的例子不再是简单的echo服务Q而是有格式(因此引入codecQ、多q接之间会交换数据的|络E序Q更接近业务场景Q也借机讲解如何避免TCP|络~程的常见陷阱。ƈ且在CZ代码中给Z分布式单词计数、多机求中位数等E微复杂一点的E序? </li><li>在C++斚wQ这本书没有介绍动态链接库热更新这U?#8220;高”技术,而是说明Q在分布式系l中Qؓ了部|方便,应该从源码编译全部的库,与主E序链接Z个standalone的可执行文gQ以减小对运行环境的依赖Q第10章)。第11章还讨论了程序库与应用程序之间的接口设计?/li></ul> <p>“信息”按照香农的定义,?#8220;减少不确定?#8221;Q这本书包含的信息正是减选用~程设施QfacilitiesQ方面的不确定性,让读者集中精力攻克本质问题。这本书介绍的方法不一定对于每个应用场景都是最好的Q但肯定是简便易行的Q是旉成本、功能、性能的一U合理折中? </p><hr size="1" width="33%" align="left" /> <p><a href="#_ftnref1_3988" name="_ftn1_3988">[1]</a> q本书前a的第一句话“本书主要讲述采用C C++ ?x86-64 Linux 上编写多U程 TCP |络服务E序的主常规技?#8221;Q封面印着“C在多核时代采用现?C++ ~写多线E?TCP |络服务器的正规做法”? </p><p><a href="#_ftnref2_3988" name="_ftn2_3988">[2]</a> 前言写到Q读者应该已l大致读q《现代操作系l》、《UNIX 环境高~程》、《UNIX |络~程》、《C++ Primer》或与之内容相近的书c,熟?zhn)基本概念Qƈ掌握 Pthreads ?Sockets API 的常规用法? </p><img src ="http://www.shnenglu.com/Solstice/aggbug/201891.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/Solstice/" target="_blank">陈硕</a> 2013-07-17 11:17 <a href="http://www.shnenglu.com/Solstice/archive/2013/07/17/201891.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Z么多U程d shared_ptr 要加锁?http://www.shnenglu.com/Solstice/archive/2013/01/28/197597.html陈硕陈硕Sun, 27 Jan 2013 21:15:00 GMThttp://www.shnenglu.com/Solstice/archive/2013/01/28/197597.htmlhttp://www.shnenglu.com/Solstice/comments/197597.htmlhttp://www.shnenglu.com/Solstice/archive/2013/01/28/197597.html#Feedback7http://www.shnenglu.com/Solstice/comments/commentRss/197597.htmlhttp://www.shnenglu.com/Solstice/services/trackbacks/197597.html陈硕Qgiantchen_AT_gmail_DOT_comQ?/p>

2012-01-28

我在《Linux 多线E服务端~程Q?muduo C++ |络库》第 1.9 ?#8220;再论 shared_ptr 的线E安?#8221;中写道:

Qshared_ptrQ的引用计数本n是安全且无锁的,但对象的d则不是,因ؓ shared_ptr 有两个数据成员,d操作不能原子化?/font>Ҏ(gu)文档Q?/font>http://www.boost.org/doc/libs/release/libs/smart_ptr/shared_ptr.htm#ThreadSafetyQ, shared_ptr 的线E安全别和内徏cd、标准库容器、std::string 一P卻I

• 一?shared_ptr 对象实体可被多个U程同时dQ文档例1Q;

• 两个 shared_ptr 对象实体可以被两个线E同时写入(?Q,“析构”写操作Q?/font>

如果要从多个U程d同一?shared_ptr 对象Q那么需要加锁(?~5Q?/font>

h意,以上?shared_ptr 对象本n的线E安全别,不是它管理的对象的线E安全别?/font>

后文Qp.18Q则介绍如何高效地加锁解锁。本文则具体分析一下ؓ什?#8220;因ؓ shared_ptr 有两个数据成员,d操作不能原子?/font>”使得多线E读写同一?shared_ptr 对象需要加?/font>。这个在我看来显而易见的l论g也有人抱有疑问,那将DN性的后果。本文以 boost::shared_ptr ZQ与 std::shared_ptr 可能略有区别?/p>

shared_ptr 的数据结?/h1>

shared_ptr 是引用计数型Qreference countingQ智能指针,几乎所有的实现都采用在堆(heapQ上放个计数|countQ的办法Q除此之外理Zq有用@环链表的办法Q不q没有实例)。具体来_shared_ptr<Foo> 包含两个成员Q一个是指向 Foo 的指?ptrQ另一个是 ref_count 指针Q其cd不一定是原始指针Q有可能?class cdQ但不媄响这里的讨论Q,指向堆上?ref_count 对象。ref_count 对象有多个成员,具体的数据结构如?1 所C,其中 deleter ?allocator 是可选的?/p>

sp0

?1Qshared_ptr 的数据结构?/p>

Z化ƈH出重点Q后文只d use_countQ?/p>

sp1

以上?shared_ptr<Foo> x(new Foo); 对应的内存数据结构?/p>

如果再执?shared_ptr<Foo> y = x; 那么对应的数据结构如下?/p>

sp2

但是 y=x 涉及两个成员的复Ӟq两步拷贝不会同Ӟ原子Q发生?/p>

中间步骤 1Q复?ptr 指针Q?/p>

sp3

中间步骤 2Q复?ref_count 指针Q导致引用计数加 1Q?/p>

sp4

步骤1和步?的先后顺序跟实现相关Q因此步?2 里没有画?y.ptr 的指向)Q我见过的都是先1??/p>

既然 y=x 有两个步骤,如果没有 mutex 保护Q那么在多线E里有 race condition?/p>

多线E无保护d shared_ptr 可能出现?race condition

考虑一个简单的场景Q有 3 ?shared_ptr<Foo> 对象 x、g、nQ?/p>

  • shared_ptr<Foo> g(new Foo); // U程之间׃n?shared_ptr
  • shared_ptr<Foo> x; // U程 A 的局部变?/li>
  • shared_ptr<Foo> n(new Foo); // U程 B 的局部变?/li>

一开始,各安其事?/p>

sp5

U程 A 执行 x = g; Q即 read gQ,以下完成了步?1Q还没来及执行步?2。这时切换到?B U程?/p>

sp6

同时~程 B 执行 g = n; Q即 write GQ,两个步骤一起完成了?/p>

先是步骤 1Q?/p>

sp7

再是步骤 2Q?/p>

sp8

q是 Foo1 对象已经销毁,x.ptr 成了I?zhn)指针Q?/p>

最后回到线E?AQ完成步?2Q?/p>

sp9

多线E无保护地读?gQ造成?#8220;x 是空(zhn)指?#8221;的后果。这正是多线E读写同一?shared_ptr 必须加锁的原因?/p>

当然Qrace condition q不止这一U,其他U程交织QinterweavingQ有可能会造成其他错误?/p>

思考,假如 shared_ptr ?operator= 实现是先复制 ref_countQ步?2Q再复制 ptrQ步?1Q,会有哪些 race conditionQ?/p>

杂项

shared_ptr 作ؓ unordered_map ?key

如果?boost::shared_ptr 攑ֈ unordered_set 中,或者用?unordered_map ?keyQ那么要心 hash table 退化ؓ链表?a title="http://stackoverflow.com/questions/6404765/c-shared-ptr-as-unordered-sets-key/12122314#12122314" >http://stackoverflow.com/questions/6404765/c-shared-ptr-as-unordered-sets-key/12122314#12122314

直到 Boost 1.47.0 发布之前Qunordered_set<std::shared_ptr<T> > 虽然可以~译通过Q但是其 hash_value ?shared_ptr 隐式转换?bool 的结果。也是_如果不自定义hash函数Q那?unordered_{set/map} 会退化ؓ链表?a title="https://svn.boost.org/trac/boost/ticket/5216" >https://svn.boost.org/trac/boost/ticket/5216

Boost 1.51 ?boost/functional/hash/extensions.hpp 中增加了有关重蝲Q现在只要包含这个头文gp安全高效C?unordered_set<std::shared_ptr> 了?/p>

q也?muduo ?examples/idleconnection CZ要自己定?hash_value(const boost::shared_ptr<T>& x) 函数的原因(书第 7.10.2 节,p.255Q。因?Debian 6 Squeeze、Ubuntu 10.04 LTS 里的 boost 版本都有q个 bug?/p>

Z么图 1 中的 ref_count 也有指向 Foo 的指针?

shared_ptr<Foo> sp(new Foo) 在构?sp 的时候捕获了 Foo 的析构行为。实际上 shared_ptr.ptr ?ref_count.ptr 可以是不同的cdQ只要它们之间存在隐式{换)Q这?shared_ptr 的一大功能。分 3 Ҏ(gu)_

1. 无需虚析构;假设 Bar ?Foo 的基c,但是 Bar ?Foo 都没有虚析构?/p>

shared_ptr<Foo> sp1(new Foo); // ref_count.ptr 的类型是 Foo*

shared_ptr<Bar> sp2 = sp1; // 可以赋|自动向上转型Qup-castQ?/p>

sp1.reset(); // q时 Foo 对象的引用计数降?1

此后 sp2 仍然能安全地理 Foo 对象的生命期Qƈ安全完整地释?FooQ因为其 ref_count C?Foo 的实际类型?/p>

2. shared_ptr<void> 可以指向q安全地理Q析构或防止析构QQ何对象;muduo::net::Channel class ?tie() 函数׃用了q一Ҏ(gu),防止对象q早析构Q见?7.15.3 节?/p>

shared_ptr<Foo> sp1(new Foo); // ref_count.ptr 的类型是 Foo*

shared_ptr<void> sp2 = sp1; // 可以赋|Foo* ?void* 自动转型

sp1.reset(); // q时 Foo 对象的引用计数降?1

此后 sp2 仍然能安全地理 Foo 对象的生命期Qƈ安全完整地释?FooQ不会出?delete void* 的情况,因ؓ delete 的是 ref_count.ptrQ不?sp2.ptr?/p>

3. 多ѝ?/strong>假设 Bar ?Foo 的多个基cM一Q那么:

shared_ptr<Foo> sp1(new Foo);

shared_ptr<Bar> sp2 = sp1; // q时 sp1.ptr ?sp2.ptr 可能指向不同的地址Q因?Bar subobject ?Foo object 中的 offset 可能不ؓ0?/p>

sp1.reset(); // 此时 Foo 对象的引用计数降?1

但是 sp2 仍然能安全地理 Foo 对象的生命期Qƈ安全完整地释?FooQ因?delete 的不?Bar*Q而是原来?Foo*。换句话_sp2.ptr ?ref_count.ptr 可能h不同的|当然它们的类型也不同Q?/p>

Z么要量使用 make_shared()Q?/h2>

Z节省一ơ内存分配,原来 shared_ptr<Foo> x(new Foo); 需要ؓ Foo ?ref_count 各分配一ơ内存,现在?make_shared() 的话Q可以一ơ分配一块够大的内存,?Foo ?ref_count 对象容n。数据结构是Q?/p>

sp10

不过 Foo 的构造函数参数要传给 make_shared()Q后者再传给 Foo::Foo()Q这只有?C++11 里通过 perfect forwarding 才能完美解决?/p>

(.?)



陈硕 2013-01-28 05:15 发表评论
]]> ҹþþþþ| þۺϸϾþù| ƷۺϾþþþþ97| 99þþƷֻоƷ | 91þþƷһ| 91þþžվ| þþƷŮAV| ޾Ʒþþþsm| Ƭѹۿþ| ҹƷþ| ɫ͵͵888ŷƷþþ| þۺϺݺۺϾþ| þþþ99ƷƬţţӰ| þþƷAVӰԺ| ޾Ʒ99þþþĻ | þþþ99ƷƬ| þݺҹҹվ| ŷͽxxxxѿþþ| ƷþӰԺ| þˬˬƬAV| ŷ޾þav| ҹӰþ| պŷþ| 91þþƷ91þɫ| av˾þۺɫ| þþþþùƷ| þþþƷ| Ʒþþþ9999| ˾þþƷ| aëƬþѲ| 99þþƷһ| Ʒþþþþþþ| ƷһþþƷɬ | 2021þùԲľƷ| þþƷҹһ | þþѹ۳ӰԺ| ޾þһ| ޹˾Ʒþþùһ | þݺҹҹ2O2O| þþþƷsmվ| þ99Ʒ鶹լլ|