苏林

一程序, 一世界

用户操作
[即时聊天] [发私信] [加为好友]
苏林ID:Slin000
104697次访问,排名894,好友149人,关注者158人。
仔细思考,踏实做事
MSN:hotsoo@hotmail.com


Slin000的文章
原创 35 篇
翻译 0 篇
转载 0 篇
评论 617 篇
苏林的公告

最近评论
chinazjf:袁老很值得敬佩。
有目的不等于就一定要被人鄙视。
Ubuntu如果有一天能在中国和windows竞争,我想真应该是中国人的福气。
yxt:linux用来做服务器相当好,客户机就要差一些。
tengel:老袁鼓吹 Ubuntu 很好,自由软件就是要有人鼓吹才能普及。
至少很多人因为老袁知道了还有 Ubuntu 还有 Linux,足够了。
tengel:老袁鼓吹 Ubuntu 很好,自由软件就是要有人鼓吹才能普及。
至少很多人因为老袁知道了还有 Ubuntu 还有 Linux,足够了。
chengchuan:哈哈,又被忽悠了!
文章分类
    收藏
      相册
      友情链接
      爱生活,爱读书--杨福川 图灵
      存档
      订阅我的博客
      XML聚合  FeedSky
      订阅到鲜果
      订阅到Google
      订阅到抓虾
      订阅到BlogLines
      订阅到Yahoo
      订阅到GouGou
      订阅到飞鸽
      订阅到Rojo
      订阅到newsgator
      订阅到netvibes

      原创 C++析构函数妙用-- 不会忘记的Unlock收藏

      新一篇: 在ToolBar上使用半透明图像 | 

      C++中,类的析构函数被解释为用于销毁 对象的代码块,在对象将被从内存中清除之前调用。而事实上,利用析构函数的调用时机,可以做很多普通的过程控制代码很难做到的 事情。

      比如在多线程程序中的锁。在加锁和开锁的过程中,必须非常小心地配对,稍有不慎就 会少了开锁次数,使资源锁得不到打开。在有复杂控制的程序体中,这种维护是很烦琐的。

      Windows程序为例,在类 CWithLock中使用临界区定义一个锁,成员函数fun将被多个线程调 用。

        CWithLock

          {

              public:

                 CWithLock();

                 ~CWithLock();

       

                 CRITICAL_SECTION  m_csAccessLock; // 作为锁的临界区

                   

                 void fun();     // 将被多线程调用的成员函数  

      };

      简单的情况下,在fun函数的定义中我们可 以简单地使用锁,如下:

      void fun()

      {

             EnterCriticalSection(&m_csAccessLock);

          // 函数体

          // 。。 。。

          LeaveCriticalSection(&m_csAccessLock);

      }

      然而,当函数体控制很复杂的时候,我们不得不在每个return语句前加上开锁语句,如下:

      void fun()

      {

             EnterCriticalSection(&m_csAccessLock);

          // 函数体

          if(..)

      {

             // 。。。。

      LeaveCriticalSection (&m_csAccessLock);

      return;

      }else

      {

          try

      {

             if ()

      {

      // 。。。。

      LeaveCriticalSection(&m_csAccessLock);

      return;

      }

      }catch()

      {

                 // 。。。。

      LeaveCriticalSection (&m_csAccessLock);

      return;

      }

      }

          // 。。 。。

          LeaveCriticalSection(&m_csAccessLock);

      }

      可以看出,随着程序控制的复杂化,不得不在多个地方增加开锁代码。这种多处都不得 不执行的相同操作增加了代码失误的可能性,以日后程序的维护和修改过程中,也会造成很多麻烦。如果在fun的执行过程中要使用多个共享资源,情况将更加糟糕。当代码失误存在于一个很少执行到的代码分支里 时,无疑是给程序埋下了一个定时炸弹。

      加锁和开锁的函数调用代码看起来也很像,在我的程序生涯中还遇到喜欢“复制 -粘贴”的同事把这两个函数写错的事情。发现这个错误所用的代价可是很大的。

      有没有办法可以放松一下代码人员紧张的神经呢?答案是有,而且不止一种。

      第一种,利用被受垢病的goto语句。把所有的开锁 工作放到fun函数的尾部,把所有的return语句换成 goto语句,跳到开锁的位置。如下:

      void fun()

      {

             EnterCriticalSection(&m_csAccessLock);

          // 函数体

          if(..)

      {

             // 。。。。

      goto end;

      }else

      {

          try

      {

             if ()

      {

      // 。。。。

      goto end;

      }

      }catch()

      {

                 // 。。。。

      goto end;

      }

      }

          // 。。 。。

          end:

          LeaveCriticalSection(&m_csAccessLock);

      }

      看起来好些了,但实际操作过程中,goto的使用给 代码也会带来很多麻烦。如goto不能跳过变量的声明;无法在函数休内动态地对某一资源开锁,以减少上 锁时间;所有的变量都声明的第一个goto之前,代码看上去也很丑,至少可读性会受到响。

      第二种方法,使用try-catch异常处理机制中的 __finally代码块。把函数体作为try代码块的一部分,在这里可以随意调用 return返回。把开锁语句放进__finally代码块中,可以保证最后总 会调用到开锁语句。

      这种方法同样问题多多,不只是难看,而且也同样不够灵活。

       

      最好的方法当然是第三种,也就是利用析构函数来解锁。在详述这种方法前,我们先看 看函数体或程序块的执行过程。

      一个程序块就是大括号“{}”内的整个代码块。一 个函数体也是一个程序块。程序块内声明的变量,在声明时执行该变量的构造函数;当控制离开程序块时,无论执行到 “}”,还是使用breakreturn语句 跳出,每个在程序块里声明的变量都将被析构,他们的析构函数都将被调用。

      利用这个特点,如果我们使用一个对象的实例来加锁,并在这个对象的析构函数中解锁 ,那么我们将无需显式地声明解锁过程,并总能保证加锁状态只在程序块里有效。

      我们设计一个简单的类来实现这个功能:

        CLock

          {

              public:

                  CLock(CRITICAL_SECTION *p_door) // 在构造函数中加锁

      {

      if (NULL != (m_pDoor = p_door))

          EnterCriticalSection(m_pDoor);

      }

                 ~CLock()                    // 在析构函数中开锁

      {

      if (NULL != m_pDoor))

          LeaveCriticalSection (m_pDoor);

      }

      privated:

            CRITICAL_SECTION * m_pDoor;

      };

      使用CLock类之后,我们将fun的函数体改写一下:

      void fun()

      {

             CLock Lock(&m_csAccessLock); // 使用CLock的构造函数加锁

          // 函数体

          if(..)

      {

             // 。。。。

      return; // 无需再开锁

      }else

      {

          try

      {

             if ()

      {

      // 。。。。

      return; // 无需再开锁

      }

      }catch()

      {

                 // 。。。。

      return; // 无需再开锁

      }

      }

          // 。。 。。// 无需再开锁

      }

      我们可以看到,除了程序的第一行以外,其余地方均和普通的单线程程序没有什么不 同。程序员不用在担心开锁的问题,可以说有了一个永远不会忘记的Unlock

      不仅如此,我们还可以使用“{}”来界定 锁的有效范围,可以在任何地方随时锁定其它的资源,并在使用结束的时候,自动开锁。如果要同时在一个代码块里锁定多个资源,析 构函数还能保证开锁的顺序,在一定程度上避免死锁。

      上述CLock代码只是一个例子,并不是最好的设计 ,并且也不能满足所有的算法要求。在有些特殊的算法中,就要求资源在一个程序块中加锁,而在另一个程序块中解锁。另外还要增加 TryLock功能,使线程在访问被锁的资源后,仍能运行。

      在考虑了所有的需求之后,可以设计出更好的类来实现锁。下面给出一个笔者使用的 类,仅供参考。

      //******** CWithLock.h ***********************

      被共享实例的类

          class CLock;

          class  CWithLock

          {

          public:

             CWithLock ();

             ~CWithLock ();

             HANDLE GetOwnerThread();

       

          protected:

             CRITICAL_SECTION  m_csAccess;

       

             inline void Lock();

             inline void UnLock();

             inline BOOL TryLock();

       

             friend class CLock;

          };

       

      //******** CWithLock.cpp ***********************

      CWithLock::CWithLock()

      {

          InitializeCriticalSection( &m_csAccess );

      }

       

      CWithLock::~CWithLock()

      {

          DeleteCriticalSection( &m_csAccess );

      }

       

      void CWithLock::Lock()

      {

          EnterCriticalSection (&m_csAccess);

      }

       

          LeaveCriticalSection (&m_csAccess);

      }

       

      BOOL CWithLock::TryLock()

      {

          return TryEnterCriticalSection(&m_csAccess);

      }

       

      HANDLE CWithLock::GetOwnerThread()

      {

          return m_csAccess.OwningThread;

      }

      //******** CLock.h ***********************

          class CWithLock;

          // 用来锁 CWithLock实例的类

          class  CLock

          {

          public:

             CLock( CWithLock &door, bool bLook = true);

             ~CLock ();

       

             void Lock();

             void UnLock();

             BOOL TryLock ();

       

             static void Lock(CWithLock &door);

             static void UnLock(CWithLock &door);

          private:

             bool m_bLocked;

             CWithLock * m_pDoor;

          };

       

      //******** CLock.cpp ***********************

      CLock::CLock( CWithLock &door, bool b_lock)

      {

          m_pDoor = &door;

          m_bLocked = b_lock;

          if (m_bLocked)

             Lock (*m_pDoor);

      }

       

      CLock::~CLock()

      {

          if (m_bLocked)

             UnLock (*m_pDoor);

      }

       

      void CLock::Lock()

      {

          if(!m_bLocked)

          {

             m_bLocked = true;

             Lock (*m_pDoor);

          }

      }

       

          if (m_bLocked)

          {

             m_bLocked = false;

             UnLock (*m_pDoor);

          }

      }

       

      BOOL CLock::TryLock()

      {

          if(!m_bLocked)

          {

             if ( m_pDoor->TryLock() )

             {

                 m_bLocked = true;

                 return TRUE;

             }     

             return FALSE;

          }

          return TRUE; 

      }

       

          door.Lock();

      }

       

          door.UnLock();

      }

      作者:苏林

      发表于 @ 2007年12月14日 17:55:00|评论(loading...)|编辑

      新一篇: 在ToolBar上使用半透明图像 | 

      评论:没有评论。

      发表评论  


      登录
      Csdn Blog version 3.1a
      Copyright © 苏林