<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-2565748418597820592</id><updated>2011-06-08T14:17:27.232+08:00</updated><category term='C++'/><category term='加锁'/><category term='多线程'/><title type='text'>C++之霓服华裳</title><subtitle type='html'>C++技术的华丽舞台</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://flyfinger.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2565748418597820592/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://flyfinger.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>非典型秃子</name><uri>http://www.blogger.com/profile/09485911740177123523</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>2</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2565748418597820592.post-1669103265007049402</id><published>2007-03-29T09:57:00.000+08:00</published><updated>2007-03-29T16:11:23.236+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='多线程'/><category scheme='http://www.blogger.com/atom/ns#' term='加锁'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>奇技淫巧C++之方法代理</title><content type='html'>&lt;h1 style="text-align: center;"&gt;奇技淫巧C++之方法代理&lt;/h1&gt;author:非典型秃子&lt;br /&gt;&lt;br /&gt;　　如果你有编写多线程程序的经历，遇到过需要共享对象的情况吗？比如，你想在两个线程中操作同一个容器，你会怎么做呢？是在每个地方都小心翼翼地加锁，还是封装一个来用？两种方法我都用过，但我比较青睐封装一个的办法，例如，封装一个带锁的队列，用于线程间的通信．&lt;br /&gt;　　让我们先看看直接操作锁和对象的代码：&lt;br /&gt;//declare:&lt;br /&gt;std::Container cont;&lt;br /&gt;LockType contLock;&lt;br /&gt;...&lt;br /&gt;//using:&lt;br /&gt;contLock.lock();&lt;br /&gt;cont.XXX();&lt;br /&gt;contLock.unlock();&lt;br /&gt;　　嗯，using下面的加锁和解锁的方法太老土，而且最关键的，还不安全，让我们稍加改善：&lt;br /&gt;ScopedLock guard(contLock);  //在ScopedLock的构造函数中lock，在析构函数中unlock&lt;br /&gt;cont.XXX();&lt;br /&gt;  看上去稍好了一点，然而有两个小的缺点。前面提到共享对象，第一个缺点就是我们需要共享两个对象，容器和锁，这使得管理和传递共享对象都变得麻烦起来。另 一个缺点是，加锁的动作需要小心谨慎，千万别忘了。可惜，即使忘了，我们也不会从编译器这里得到任何帮助。这两个不便，都会促使我们考虑是不是把对象和锁 封装起来更好？很多时候，我们确实是这么做的，看一个deque的例子（省略std）.&lt;br /&gt;template&lt;typename alloc=" allocator&lt;T"&gt; &gt;&lt;br /&gt;class shared_deque{&lt;br /&gt;  dequeT, Alloc&gt; m_cont;&lt;br /&gt;  LockType   m_lock;&lt;br /&gt;  ...&lt;br /&gt;  void push_back(const_reference val){&lt;br /&gt;     ScopedLock guard(m_lock);&lt;br /&gt;      m_cont.push_back(val);&lt;br /&gt;  }&lt;br /&gt;  ....&lt;br /&gt;};&lt;br /&gt;  呼，终于好了，我们现在有了一个好用的shared_queue了。只是，类似那个push_back的东西，重复了几十遍，很无聊的，还有必要对 list也来一遍吗？算了吧！万幸，没有用basic_string，那家伙可有１００多个成员函数。有没有办法简化一下工作呢？重复的东西总是应该交给 计算机来做是不是？还好，C++正好能帮助我们实现这一目标，这个手法在MCD中被寥寥数语带过，就是那神奇的operator-&gt;()．&lt;br /&gt; 让我们回顾一下operator-&gt;的用法：用于对象指针，提取对象成员，函数或对象．许多smart pointer地实现都重载了这个运算符，从而可以模拟指针的语法．一般的重载形式是这样的：&lt;br /&gt;cv1 T* operator-&gt;() cv2&lt;br /&gt; 这里返回的是T*类型，如果返回的不是指针类型呢？C++标准对此有特别规定，会继续调用返回值的operator-&gt;()方法，直到最终解析出一个指针类型．假定有下面的operator-&gt;展开过程：&lt;br /&gt;object a--&gt;b--&gt;T pointer&lt;br /&gt;(请 注意一下a,b,T对象的生命期，确定我们是安全地在使用这些对象．)a对象的operator-&gt;返回临时对象b,b对象的operator -&gt;返回最终类型T*.那么，b对象必须在其T* operator-&gt;()调用之前创建好，而在T::XXX()方法调用之后被销毁，因为b是临时对象．嗯，好了，有点方向了：在b的构造函数中加 锁，在析构中解锁，就可以在调用T::XXX()方法时自动实现加锁可解锁了．&lt;br /&gt;　　拓展一下思维，我们必须局限于锁和容器吗？不必．这个手法的本质效果是什么？就是在调用一个方法之前，插入一些操作，调用之后再插入一些操作（稍显遗憾的是，我们无法知道被调用的到底是什么方法）．但是，这也够我们做许多事情了．实现如下：&lt;br /&gt;#include&lt;br /&gt;  template&lt;br /&gt;  &lt; scopedtype =" typename"&gt;&lt;br /&gt;  class call_proxy&lt;br /&gt;  {&lt;br /&gt;  public:&lt;br /&gt;      typedef call_proxy self_type;&lt;br /&gt;      typedef Pointer pointer_type;&lt;br /&gt;      typedef Monitor monitor_type;&lt;br /&gt;      typedef ScopedType scoped_type;&lt;br /&gt;&lt;br /&gt;      typedef typename boost::call_traits&lt;pointer_type&gt;::param_type param_type;     &lt;br /&gt;      typedef monitor_type&amp; monitor_reference;&lt;br /&gt;&lt;br /&gt;  private:     &lt;br /&gt;      struct proxy{&lt;br /&gt;          proxy(self_type* host) : m_host(host), m_s(host-&gt;m_monitor){};&lt;br /&gt;          pointer_type operator-&gt;(){&lt;br /&gt;              return m_host-&gt;m_obj;&lt;br /&gt;          }&lt;br /&gt;      private:&lt;br /&gt;          self_type* m_host;&lt;br /&gt;          scoped_type m_s;&lt;br /&gt;          proxy(const proxy&amp;);&lt;br /&gt;          proxy&amp; operator=(const proxy&amp;amp;amp;amp;)&lt;br /&gt;      };&lt;br /&gt;      friend struct proxy;&lt;br /&gt;  public:&lt;br /&gt;      call_proxy(param_type p, monitor_reference m) : m_obj(p), m_monitor(m){ assert(p);}&lt;br /&gt;&lt;br /&gt;      proxy operator-&gt;() {&lt;br /&gt;          return this;&lt;br /&gt;      }&lt;br /&gt;  private:&lt;br /&gt;      pointer_type m_obj;&lt;br /&gt;      boost::reference_wrapper&lt;monitor_type&gt; m_monitor;&lt;br /&gt;  };&lt;br /&gt;为 了可以和smart_pointer合作，call_proxy需要的第一个模版参数是被代理对象的指针类型，而不是自己产生指针类型，这就允许是一个 smart_pointer的类型．monitor类型本质上需要开始和结束两个方法，把它封装进ScopedType,依靠ScopedType的构造 和析构来完成．这样做的目的是避免对限制monitor的方法名称，可以看作是traits手法的简化版本．你可以自定义合适的ScopedType类 型．嵌套类proxy的构造函数的隐式转换是必要的，它可以消除额外的copy ctor的需求．&lt;br /&gt;　　上述的实现代码已经没什么神奇之处可言了．使用方法如下：&lt;br /&gt;  typedef vector&lt;int&gt; MyVector;&lt;br /&gt;  MyVector cont;&lt;br /&gt;  LockType lock;&lt;br /&gt;  typedef call_proxy&lt;myvector*,&gt; proxy_type;&lt;br /&gt;  proxy_type cont_proxy(&amp;cont, lock);&lt;br /&gt;  至此，cont_proxy可以作为一个封装好的对象使用了．嗯，当然，cont_proxy的生命周期应该比cont来得短，这个问题就让程序员去保证吧．&lt;br /&gt;剩下的问题：&lt;br /&gt;　 　call_proxy还有一些问题需要解决．我们在调用某些成员方法的时候，未必都要加锁．如果对象和锁是分离的，那么自然很容易处理．如果是手工封 装，虽然工程浩大，但是也可以在适当的地方加以特别处理．特别的，对于分离的对象和锁，我们还可以使用大粒度的锁定过程，从而改善某些性能．而这里的 call_proxy则没有这种灵活性，当然手工封装的类也无此灵活性．然而，我们还是可以改善call_proxy，从而在一定程度上获得这种灵活性的 好处，为call_proxy增加两个友元方法：&lt;br /&gt;  friend pointer_type getImp(const self_type&amp; cp){ return cp.m_obj;}&lt;br /&gt;  friend monitor_reference getMonitor(const self_type&amp; cp){ return cp.m_monitor;}&lt;br /&gt;当我们需要大粒度的机制时可以这样：&lt;br /&gt;{&lt;br /&gt;  proxy_type::scoped_type guard(getMonitor(cont_proxy));&lt;br /&gt;  getImp(cont_proxy)-&gt;XXX1();&lt;br /&gt;  getImp(cont_proxy)-&gt;XXX2();&lt;br /&gt;  ...&lt;br /&gt;}&lt;br /&gt;对于新手，可能会奇怪getImp的用途，或者忘记调用，这不够优雅．但是这样的解决方案已经比较简单了，它简化了大部分的情况，而且留了一条优化的后路．&lt;/myvector*,&gt;&lt;/int&gt;&lt;/monitor_type&gt;&lt;/pointer_type&gt;&lt;/typename&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2565748418597820592-1669103265007049402?l=flyfinger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://flyfinger.blogspot.com/feeds/1669103265007049402/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2565748418597820592&amp;postID=1669103265007049402' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2565748418597820592/posts/default/1669103265007049402'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2565748418597820592/posts/default/1669103265007049402'/><link rel='alternate' type='text/html' href='http://flyfinger.blogspot.com/2007/03/c.html' title='奇技淫巧C++之方法代理'/><author><name>非典型秃子</name><uri>http://www.blogger.com/profile/09485911740177123523</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2565748418597820592.post-7261251472924693235</id><published>2007-03-16T10:59:00.000+08:00</published><updated>2007-03-16T11:00:29.083+08:00</updated><title type='text'>庆祝</title><content type='html'>成功在blogger开博&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2565748418597820592-7261251472924693235?l=flyfinger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://flyfinger.blogspot.com/feeds/7261251472924693235/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2565748418597820592&amp;postID=7261251472924693235' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2565748418597820592/posts/default/7261251472924693235'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2565748418597820592/posts/default/7261251472924693235'/><link rel='alternate' type='text/html' href='http://flyfinger.blogspot.com/2007/03/blog-post.html' title='庆祝'/><author><name>非典型秃子</name><uri>http://www.blogger.com/profile/09485911740177123523</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
