设为首页 加入收藏

TOP

10.5.4 Tss库(1)
2013-10-07 15:05:15 来源: 作者: 【 】 浏览:70
Tags:10.5.4 Tss

10.5.4  Tss库(1)

我曾为上面提到的有关PTHREADS和Win32的TSS机制的4个问题大伤脑筋,最后我捋起袖子自己写了一个库,该库提供了我需要的功能。它包含8个函数和两个辅助类。其主函数与C和C++(www.cppentry.com)都是兼容的,如程序清单10.6所示:

程序清单10.6

  1. // MLTssStr.h - 函数被声明为 extern "C"  
  2. int     Tss_Init(void);    /* Failed if < 0. */  
  3. void    Tss_Uninit(void);  
  4. void    Tss_ThreadAttach(void);  
  5. void    Tss_ThreadDetach(void);  
  6. HTssKey Tss_CreateKey( void (*pfnClose)()  
  7.                          , void (*pfnClientConnect)()  
  8.                          , void (*pfnClientDisconnect)()  
  9.                          , Boolean bCloseOnAssign);  
  10. void    Tss_CloseKey(     HTssKey  hEntry);  
  11. void    Tss_SetSlotValue( HTssKey  hEntry  
  12.                             , void     *value  
  13.                             , void     **pPrevValue /* = NULL */);  
  14. void    *Tss_GetSlotValue(HTssKey  hEntry);  

跟所有的良好的API一样,它具有Init/Uninit1方法,以便确保在任何客户使用它之前得到正确的初始化。它还含有另外两个函数,用于attach(附加)到某个线程以及从某个线程detach(脱离),我们待会再来讨论它们。

其中4个函数采用了操纵键的习惯做法。然而,这些函数提供了额外的功能。Tss_CreateKey()的参数pfnClose接受一个可选的回调函数,以便提供线程终止时的清理能力,如果你不需要,那么传递NULL就行了。此外,如果你希望在槽位值被覆写的时候调用清理函数,就必须指定bCloseOnAssign参数为true。

pfnClientConnect和pfnClientDisconnect两个参数接受可选的回调函数,用于防止代码过早地消失。你可以以任何恰当的方式来实现这两个回调函数,只要能够确保pfnClose所指的函数在被需要时仍存在于内存中并可调用即可。我在使用该API的过程中曾遇到过这样的情况:为其他API指定Init/Uninit函数,或者去锁定和解锁一个位于内存中的动态库,或者必要时二者兼而有之。

Tss_CloseKey()和Tss_GetSlotValue()的语义跟你期望的一样。然而,Tss_SetSlotValue()跟它的PTHREADS/Win32版本相比具有一个额外的参数:pPrevValue,如果该参数是NULL的话,原先的值就会被覆盖掉,并且按照该键创建时要求的清理方式被清理。如果该参数不是NULL,任何清理操作都会被跳过,原先的值会被返回给调用者。这就允许对槽位值进行粒度更细的控制,同时在缺省情况下可提供强大的清理语义。

作为一个C API,下一步很自然是将它封装到域守卫类中去,我提供了两个。第一个是TssKey类,该类并不是特别引人注意,它只是用于简化接口,并且将RAII用于关闭键,因此我只展示其公共接口:

程序清单10.7

  1. template <typename T> 
  2. class TssKey  
  3. {  
  4. public:  
  5.   TssKey( void (*pfnClose)(T )  
  6.          , void (*pfnClientConnect)()  
  7.          , void (*pfnClientDisconnect)()  
  8.          , Boolean bCloseOnAssign = true);  
  9.   ~TssKey();  
  10. public:  
  11.   void  SetSlotValue(T value, T *pPrevValue = NULL);  
  12.   T     GetSlotValue() const;  
  13. private:  
  14.   .  . . // 成员,隐藏拷贝构造函数和赋值操作符  
  15. };  

其实现中包含了用于确保sizeof(T) == sizeof(void*)的静态断言(见1.4.7小节),这是为了防止任何想要将大对象按值保存到槽位中的错误企图。用户欲保存的值会被转型至参数化类型(即T),节省了你在客户代码中的工作。

另一个类更有趣一些。如果你使用槽位值的目的是为了创建单个实体然后去复用它,那么你通常应该遵循程序清单10.8中的模式:

程序清单10.8

  1. TssKey key_func(closeThing, . . .);  
  2. . . .  
  3. OneThing const &func(Another *another)  
  4. {  
  5.   OneThing *thing = (OneThing*)key_func.GetSlotValue();  
  6.   if(NULL == thing)  
  7.   {  
  8.     thing = new OneThing(another);  
  9.     key_func.SetSlotValue(thing);  
  10.   }  
  11.   else  
  12.   {  
  13.     thing->Method(another);  
  14.   }  
  15.   return *thing;  
  16. }  

然而,如果该函数更复杂一些(大多数时候都是这样),那么其内部就会出现多处可能改变槽位值的地方。每一处都存在着资源泄漏的可能,这种资源泄漏是因在调用SetSlotValue()之前过早返回而导致的。出于这个原因,我提供了域守卫类TssSlotScope,见程序清单10.9。我得承认我对该类有些过分的偏爱,因为它是纯粹的RAII的体现。

程序清单10.9

  1. template <typename T> 
  2. class TssSlotScope  
  3. {  
  4. public:  
  5.   TssSlotScope(HTssKey hKey, T &value)  
  6.     : m_hKey(hKey)  
  7.     , m_valueRef(value)  
  8.     , m_prevValue((value_type)Tss_GetSlotValue(m_hKey))  
  9.   {  
  10.     m_valueRef = m_prevValue;  
  11.   }  
  12.   TssSlotScope(TssKey<T> key, T &value);  
  13.   ~TssSlotScope()  
  14.   {  
  15.     if(m_valueRef != m_prevValue)  
  16.     {  
  17.       Tss_SetSlotValue(m_hKey, m_valueRef, NULL);  
  18.     }  
  19.   }  
  20. private:  
  21.   TssKey  m_key;  
  22.   T       &m_valueRef;  
  23.   T const m_prevValue;  
  24. // Not to be implemented  
  25. private:  
  26.   . . . // 隐藏拷贝构造函数和赋值操作符  
  27. };  

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇10.5.2 线程相关的数据/线程局部.. 下一篇10.4.2 匿名synchronized

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: