找回密码
 用户注册

QQ登录

只需一步,快速开始

查看: 8186|回复: 2

ACE TSS 自动清理机制分析与应用

[复制链接]
发表于 2014-3-17 15:14:06 | 显示全部楼层 |阅读模式
本帖最后由 yunh 于 2014-3-28 15:54 编辑

        TSS (Thread Specific Storage),在 Windows 平台上称为 TLS (Thread Local Storage),简单的概括就是基于线程的专有数据,进程中的所有线程都访问一个全局变量,但不同线程访问的却是基于自己线程的数据,不会相互干扰,也不用加锁保护,当然实际上它们也就不是同一份数据了,所以一个线程的修改对于另一个线程是不可见的。对于想要使用 per thread 的场景,是最好的选择,例如 C 运行库的 errno。
        Unix like 平台上的 TSS 具有线程退出时自动清理的能力,在 pthread_/thr_keycreate 调用中可以指定一个清理函数,当线程退出时,会对每个线程专有的数据执行该清理函数。
  1. int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
复制代码
       但 Windows 上就没有这么便利了,对等的 TlsAlloc 就没有相应参数:
  1. DWORD TlsAlloc(void);
复制代码
       用户必需在线程退出前显示的删除 TSS 中的专用数据,否则会产生内存泄漏 (如果是动态分配内存的话)。
        ACE 的封装消除了平台差异,它也可以指定一个 destructor 供线程退出前销毁专用数据:
  1. int thr_keycreate (ACE_thread_key_t *key,
  2.                      ACE_THR_DEST,
  3.                      void *inst = 0);
复制代码
       为了证实这一点,写一个小的测试程序在 Windows 平台上运行一下,如果指定的 destructor 被调用了,就证明线程专用数据被自动清理了,这里分四种场景分别模拟不同情况下线程的退出。下面首先给出测试代码:
  1. #include "stdafx.h"
  2. #include "ace/Log_Msg.h"
  3. #include "ace/OS_NS_unistd.h"
  4. //#define USE_THR_MGR
  5. //#define SCENE_MAIN_TERM
  6. //#define SCENE_MAIN_EXIT
  7. //#define SCENE_THR_EXIT
  8. #if defined (USE_THR_MGR)
  9. #include "ace/Thread_Manager.h"
  10. #else
  11. #include "ace/OS_NS_thread.h"
  12. #endif
  13. ACE_thread_key_t key = 0;
  14. void tss_cleanup_func (void *data)
  15. {
  16.     ACE_DEBUG ((LM_DEBUG, "(%t) tss data (%u) cleaned up.\n", *(int *)data));
  17.     delete (int *)data;
  18. }
  19. ACE_THR_FUNC_RETURN thr_func (void *arg)
  20. {
  21.     ACE_DEBUG ((LM_DEBUG, "(%t) start\n"));
  22.     ACE_OS::thr_setspecific (key, new int (ACE_OS::thr_self ()));
  23. #if defined (SCENE_MAIN_TERM)
  24.     ACE_OS::sleep (10);
  25. #elif defined (SCENE_THR_EXIT)
  26.     ACE_DEBUG ((LM_DEBUG, "(%t) exit\n"));
  27.     ACE_OS::thr_exit (2);
  28. #endif
  29.     ACE_DEBUG ((LM_DEBUG, "(%t) end\n"));
  30.     return 0;
  31. }
  32. int ACE_TMAIN(int argc, ACE_TCHAR* argv[])
  33. {
  34.     ACE_DEBUG ((LM_DEBUG, "(%t) main start\n"));
  35.     ACE_OS::thr_keycreate (&key, tss_cleanup_func);
  36. #if defined (USE_THR_MGR)
  37.     ACE_Thread_Manager::instance ()->spawn (thr_func);
  38. #else
  39.     ACE_hthread_t hid = 0;
  40.     ACE_OS::thr_create (thr_func, 0, THR_NEW_LWP | THR_JOINABLE, 0, &hid);
  41. #endif
  42. #if defined (SCENE_MAIN_TERM)
  43.     ACE_OS::sleep (3);
  44. #elif defined (SCENE_MAIN_EXIT)
  45.     ACE_OS::exit (1);
  46. #else
  47. #  if defined (USE_THR_MGR)
  48.     ACE_Thread_Manager::instance ()->wait ();
  49. #  else
  50.     ACE_OS::thr_join (hid, 0);
  51. #  endif
  52. #endif
  53.     ACE_DEBUG ((LM_DEBUG, "(%t) main end\n"));
  54.     return 0;
  55. }
复制代码
       这里使用编译开关 USE_THR_MGR 来切换线程的启动方式,但无论使用哪种启动方式,程序的输出应当是一致的。
        场景一,线程函数运行完毕,线程自动结束。保持测试程序所有编译开关关闭,编译运行,输出如下:
  1. (6304) main start
  2. (6128) start
  3. (6128) end
  4. (6128) tss data (6128) cleaned up.
  5. (6304) main end
  6. 请按任意键继续. . .
复制代码
       子线程 6128 的 TSS 清理函数确实被调用了!
        场景二,线程函数调用 ACE_OS::thr_exit 主动结束自己 (注意不是 ACE_OS::exit,后者是专门给主线程使用的)。打开编译开关 SCENE_THR_EXIT,编译运行,输出如下:
  1. (3576) main start
  2. (7232) start
  3. (7232) exit
  4. (7232) tss data (7232) cleaned up.
  5. (3576) main end
  6. 请按任意键继续. . .
复制代码
       这种情况下 TSS 清理函数也被调用了!
        场景三,线程函数还在执行,主线程线程函数运行完毕自动退出,导致进程中所有线程结束。打开编译开关 SCENE_MAIN_TERM,关闭其它开关,编译运行,输出如下:
  1. (1480) main start
  2. (4808) start
  3. (1480) main end
  4. 请按任意键继续. . .
复制代码
       这种情况下清理函数没有被调用 :(
        场景四,线程函数还在执行,主线程调用 ACE_OS::exit 退出 (注意不是 ACE_OS::thr_exit,后者是专门给 ACE 管理的线程使用的),导致进程中所有线程结束。打开编译开关 SCENE_MAIN_EXIT,关闭其它,输出如下:
  1. (3324) main start
  2. 请按任意键继续. . .
复制代码
       这种情况下清理函数也没有被调用。
        发生上面现象的根本原因是主线程一般不是由 ACE 管控的,所以无法在线程函数开始与结束安插代码来处理 TSS 的清理,因此最好不要在主线程中使用 ACE_OS::exit 直接退出,或不等待其它线程就结束。对于 ACE 管理的线程,它的清理工作做的还是很好的,下面着重从两种场景分析 TSS 自动清理机制是如何实现的,即线程函数执行完毕自动结束与直接调用 ACE_OS::thr_exit (或 ACE_Thread::exit,但不是 ACE_OS::exit 或 ::exit) 退出。
        从上面的输出可以看到,清理函数就是在本线程中被执行的,所以最简单的办法莫过于在清理函数中打一个断点,观察它是怎么被调用的,下面是在不开启所有编译开关时的堆栈:
  1. >    tss_scene1.exe!tss_cleanup_func(void * data=0x00207fa8)  行24    C++
  2.      ACE5.4.1d.dll!ACE_TSS_Cleanup::exit(void * __formal=0x00000000)  行772 + 0x1b 字节    C++
  3.      ACE5.4.1d.dll!ACE_OS::cleanup_tss(const unsigned int main_thread=0)  行1093    C++
  4.      ACE5.4.1d.dll!ACE_OS_Thread_Adapter::invoke()  行133 + 0x7 字节    C++
  5.      ACE5.4.1d.dll!ACE_OS_Thread_Adapter::invoke()  行110 + 0xc 字节    C++
  6.      ACE5.4.1d.dll!ace_thread_adapter(void * args=0x00207fa8)  行131 + 0xe 字节    C++
  7.      msvcr80d.dll!_callthreadstartex()  行348 + 0xf 字节    C
  8.      msvcr80d.dll!_threadstartex(void * ptd=0x00208268)  行331    C
复制代码
       ACE 启动的所有线程的线程函数默认就是 ace_thread_adapter,它又回调了可执行对象的 invoke 方法:
  1.   // Invoke the user-supplied function with the args.
  2.   ACE_THR_FUNC_RETURN status = thread_args->invoke ();
复制代码
       对于使用 Thread_Manager 的情形,这个对象是 ACE_Thread_Adapter,否则是 ACE_OS_Thread_Adapter,为了验证这一点,单独打开 USE_THR_MGR 开关,可以看到堆栈如下:
  1. >    tss_scene1.exe!tss_cleanup_func(void * data=0x002182f0)  行24    C++
  2.      ACE5.4.1d.dll!ACE_TSS_Cleanup::exit(void * __formal=0x00000000)  行772 + 0x1b 字节    C++
  3.      ACE5.4.1d.dll!ACE_OS::cleanup_tss(const unsigned int main_thread=0)  行1093    C++
  4.      ACE5.4.1d.dll!ACE_Thread_Adapter::invoke_i()  行192 + 0x7 字节    C++
  5.      ACE5.4.1d.dll!ACE_Thread_Adapter::invoke_i()  行164 + 0xc 字节    C++
  6.      ACE5.4.1d.dll!ACE_Thread_Adapter::invoke()  行93 + 0xf 字节    C++
  7.      ACE5.4.1d.dll!ace_thread_adapter(void * args=0x002182f0)  行131 + 0xe 字节    C++
  8.      msvcr80d.dll!_callthreadstartex()  行348 + 0xf 字节    C
  9.      msvcr80d.dll!_threadstartex(void * ptd=0x002185b0)  行331    C
复制代码
       好,那现在“话分两头,各表一枝”,先说使用 ACE_OS::thr_create 的情况,再讲使用 ACE_Thread_Manager::spawn 的情况,ACE_Task 的 activate 实际属于后一种情况。前者回调 ACE_OS_Thread_Adapter 的 invoke 方法:
  1.               // Call thread entry point.
  2. #if defined (ACE_PSOS)
  3.               (*func) (arg);
  4.               status = 0;
  5. #else /* ! ACE_PSOS */
  6.               status = (*func) (arg);
  7. #endif /* ACE_PSOS */
复制代码
       函数的主体就是对用户提供的线程函数进行回调 (*func)(arg),但是接着往下看,线程函数返回后,又做了哪些调用:
  1. #if defined (ACE_WIN32) || defined (ACE_HAS_TSS_EMULATION)
  2.       // Call TSS destructors.
  3.       ACE_OS::cleanup_tss (0 /* not main thread */);
  4. ……
复制代码
       不出所料,对于没有 TSS 自动清理的平台 (WIN32),通过 cleanup_tss 来完成这一切,正如之前在堆栈中看到的那样。该函数一进来,开门见山就清理 TSS:
  1. #if defined (ACE_HAS_TSS_EMULATION) || defined (ACE_WIN32) || (defined (ACE_PSOS) && defined (ACE_PSOS_HAS_TSS))
  2.   // Call TSS destructors for current thread.
  3.   ACE_TSS_Cleanup::instance ()->exit (0);
  4. #endif /* ACE_HAS_TSS_EMULATION || ACE_WIN32 || ACE_PSOS_HAS_TSS */
复制代码
       下面的代码过于琐碎,就不一一列举了,概括讲来,就是 ACE_TSS_Cleanup 类负责进程中所有 TSS 数据的记录与清理,在 thr_keycreate 当中它记录这些信息,当线程退出时,它清理它们。
        返回头来,看使用 Thread_Manager 创建的线程,在 ACE_Thread_Adapter::invoke_i 中 (被 invoke 所调用),线程函数返回后所做的工作与之前完全一致:
  1.           // Call TSS destructors.
  2.       ACE_OS::cleanup_tss (0 /* not main thread */);
复制代码
        至此二者合流了,也就是说,不论使用何种方式创建线程,最终都会调用 cleanup_tss 来清理线程专用数据。
        这是线程正常退出的情况,那调用 ACE_OS::thr_exit 的情况呢,下面看下打开 SCENE_THR_EXIT 时 (关闭其它) 的堆栈:
  1. >    tss_scene1.exe!tss_cleanup_func(void * data=0x013c7fa8)  行24    C++
  2.      ACE5.4.1d.dll!ACE_TSS_Cleanup::exit(void * __formal=0x00000000)  行772 + 0x1b 字节    C++
  3.      ACE5.4.1d.dll!ACE_OS::cleanup_tss(const unsigned int main_thread=0)  行1093    C++
  4.      ACE5.4.1d.dll!ACE_OS::thr_exit(unsigned long status=2)  行3079 + 0x7 字节    C++
  5.      tss_scene1.exe!thr_func(void * arg=0x00000000)  行35 + 0xa 字节    C++
  6.      ACE5.4.1d.dll!ACE_OS_Thread_Adapter::invoke()  行96 + 0x9 字节    C++
  7.      ACE5.4.1d.dll!ace_thread_adapter(void * args=0x013c7fa8)  行131 + 0xe 字节    C++
  8.      msvcr80d.dll!_callthreadstartex()  行348 + 0xf 字节    C
  9.      msvcr80d.dll!_threadstartex(void * ptd=0x013c8268)  行331    C
复制代码
       同理,在 ACE_OS::thr_exit 中存在清理 TSS 的过程,它们位于 _endthread 调用之前:
  1.     // Call TSS destructors.
  2.     ACE_OS::cleanup_tss (0 /* not main thread */);
复制代码
      也就是说不论是线程函数执行完毕正常退出,还是调用 ACE_OS::thr_exit 退出,都会调用 ACE_OS::cleanup_tss 进行 TSS 的自动清理工作。使用 Thread_Manager 创建的线程在调用 ACE_OS::thr_exit 时基本相同,在此基础上打开 USE_THR_MGR 开关 (即同时打开 SCENE_THR_EXIT 开关),观察堆栈:
  1. >    tss_scene1.exe!tss_cleanup_func(void * data=0x016582f0)  行24    C++
  2.      ACE5.4.1d.dll!ACE_TSS_Cleanup::exit(void * __formal=0x00000000)  行772 + 0x1b 字节    C++
  3.      ACE5.4.1d.dll!ACE_OS::cleanup_tss(const unsigned int main_thread=0)  行1093    C++
  4.      ACE5.4.1d.dll!ACE_OS::thr_exit(unsigned long status=2)  行3079 + 0x7 字节    C++
  5.      tss_scene1.exe!thr_func(void * arg=0x00000000)  行35 + 0xa 字节    C++
  6.      ACE5.4.1d.dll!ACE_Thread_Adapter::invoke_i()  行150 + 0x9 字节    C++
  7.      ACE5.4.1d.dll!ACE_Thread_Adapter::invoke()  行93 + 0xf 字节    C++
  8.      ACE5.4.1d.dll!ace_thread_adapter(void * args=0x016582f0)  行131 + 0xe 字节    C++
  9.      msvcr80d.dll!_callthreadstartex()  行348 + 0xf 字节    C
  10.      msvcr80d.dll!_threadstartex(void * ptd=0x016585b0)  行331    C
复制代码
       至 thr_func 之后的调用完全相同。注意下面几个函数的区别: ACE_OS::thr_exit、ACE_OS::exit、ACE_Thread::exit、::exit。其中 ACE_OS::thr_exit 与 ACE_Thread::exit 一致,是 ACE 创建的线程用来退出线程的,它会自动调用 TSS 清理函数,其中后者是前者的简单封装;ACE_OS::exit 是为非 ACE 管理的线程准备的,而且是主线程,所以如果不是主线程,最好不要调用它,否则会因为关闭一些 ACE 的管理对象而导致莫名其妙的问题;::exit 一般是 C library 提供的,如果直接调用它,也得不到想要的自动清理效果。
        ACE_Object_Manager::at_exit 可以注册一个对象,当进程退出时,该对象自动被销毁:
  1. static int at_exit (ACE_Cleanup *object, void *param = 0);
  2. static int at_exit (void *object,
  3.                       ACE_CLEANUP_FUNC cleanup_hook,
  4.                       void *param);
复制代码
       其实所有的单例对象都是通过这种方式实现销毁的,ACE_Singleton 中创建单例后就立即将它注册到了 Object_Manager:
  1.           if (singleton == 0)
  2.             {
  3. #endif /* ACE_MT_SAFE */
  4.               ACE_NEW_RETURN (singleton, (ACE_Singleton<TYPE, ACE_LOCK>), 0);
  5.               // Register for destruction with ACE_Object_Manager.
  6.               ACE_Object_Manager::at_exit (singleton);
  7. #if defined (ACE_MT_SAFE) && (ACE_MT_SAFE != 0)
  8.             }
复制代码
       同理,ACE_Thread_Manager 也支撑线程退出时对象的销毁,它的接口与上面的非常类似:
  1.   int at_exit (ACE_At_Thread_Exit* cleanup);
  2.   int at_exit (void *object,
  3.                ACE_CLEANUP_FUNC cleanup_hook,
  4.                void *param);
复制代码
       其实这种机制就是基于 TSS 自动清理机制,和以前一样,先看一个测试程序:
  1. #include "stdafx.h"
  2. #include "ace/Thread_Manager.h"
  3. //#define USE_EXIT
  4. void tss_cleanup (void *obj, void *param)
  5. {
  6.     ACE_DEBUG ((LM_DEBUG, "(%t) clean up 0x%x (%u).\n", obj, *(int *)param));
  7.     delete (int *)param;
  8. }
  9. ACE_THR_FUNC_RETURN thr_func (void *arg)
  10. {
  11.     ACE_DEBUG ((LM_DEBUG, "(%t) start\n"));
  12.     ACE_Thread_Manager *mgr = ACE_Thread_Manager::instance ();
  13.     mgr->at_exit (0, tss_cleanup, arg);
  14. #if defined (USE_EXIT)
  15.     ACE_DEBUG ((LM_DEBUG, "(%t) exit\n"));
  16.     ACE_OS::thr_exit (1);
  17. #endif
  18.     ACE_DEBUG ((LM_DEBUG, "(%t) end\n"));
  19.     return 0;
  20. }
  21. int ACE_TMAIN(int argc, ACE_TCHAR* argv[])
  22. {
  23.     ACE_DEBUG ((LM_DEBUG, "(%t) main start\n"));
  24.     ACE_Thread_Manager *mgr = ACE_Thread_Manager::instance ();
  25.     mgr->spawn (thr_func, new int(42));
  26.     mgr->wait ();
  27.     ACE_DEBUG ((LM_DEBUG, "(%t) main end\n"));
  28.     return 0;
  29. }
复制代码
       程序启动一个线程,并在该线程中注册一个需要在线程退出时调用的清理函数,编译运行后,输出如下:
  1. (5548) main start
  2. (3232) start
  3. (3232) end
  4. (3232) clean up 0x0 (42).
  5. (5548) main end
  6. 请按任意键继续. . .
复制代码
       可以看到,清理函数确实被调用了!而且时间就是在子线程销毁,主线程还在运行的时候。那么这套机制是如何实现的呢? 在清理函数中打一个断点,看下堆栈:
  1. >    thr_atexit.exe!tss_cleanup(void * obj=0x00000000, void * param=0x01458218)  行10    C++
  2.      ACE5.4.1d.dll!ACE_At_Thread_Exit_Func::apply()  行77 + 0x18 字节    C++
  3.      ACE5.4.1d.dll!ACE_Thread_Descriptor::at_pop(int apply=1)  行81 + 0xf 字节    C++
  4.      ACE5.4.1d.dll!ACE_Thread_Descriptor::do_at_exit()  行132 + 0xa 字节    C++
  5.      ACE5.4.1d.dll!ACE_Thread_Descriptor::terminate()  行148    C++
  6.      ACE5.4.1d.dll!ACE_Thread_Manager::exit(unsigned long status=0, int do_thr_exit=0)  行1723    C++
  7.      ACE5.4.1d.dll!ACE_Thread_Control::exit(unsigned long exit_status=0, int do_thr_exit=0)  行82 + 0x12 字节    C++
  8.      ACE5.4.1d.dll!ACE_Thread_Control::~ACE_Thread_Control()  行72    C++
  9.      ACE5.4.1d.dll!ACE_Thread_Exit::~ACE_Thread_Exit()  行89 + 0x8 字节    C++
  10.      ACE5.4.1d.dll!ACE_Thread_Exit::`scalar deleting destructor'()  + 0x16 字节    C++
  11.      ACE5.4.1d.dll!ACE_TSS<ACE_Thread_Exit>::cleanup(void * ptr=0x01458450)  行85 + 0x1c 字节    C++
  12.      ACE5.4.1d.dll!ACE_TSS_Cleanup::exit(void * __formal=0x00000000)  行772 + 0x1b 字节    C++
  13.      ACE5.4.1d.dll!ACE_OS::cleanup_tss(const unsigned int main_thread=0)  行1093    C++
  14.      ACE5.4.1d.dll!ACE_Thread_Adapter::invoke_i()  行192 + 0x7 字节    C++
  15.      ACE5.4.1d.dll!ACE_Thread_Adapter::invoke_i()  行164 + 0xc 字节    C++
  16.      ACE5.4.1d.dll!ACE_Thread_Adapter::invoke()  行93 + 0xf 字节    C++
  17.      ACE5.4.1d.dll!ace_thread_adapter(void * args=0x01458330)  行131 + 0xe 字节    C++
  18.      msvcr80d.dll!_callthreadstartex()  行348 + 0xf 字节    C
  19.      msvcr80d.dll!_threadstartex(void * ptd=0x014585f0)  行331    C
复制代码
       又是 ACE_OS::cleanup_tss!在线程函数退出时会调用它,这个已经在上面分析过了,它主要是用来清理 TSS 的,看来线程退出的清理机制确实有赖于 TSS 清理机制,经过一番源码探究,现在把它们两个的关系概括如下,ACE_Thread_Manager 中有一个静态成员 thr_exit_:
  1.   /// Global ACE_TSS (ACE_Thread_Exit) object ptr.
  2.   static ACE_TSS_TYPE (ACE_Thread_Exit) *thr_exit_;
复制代码
       它是在 set_thr_exit 方法中被设置的,而仅有 ACE_Thread_Exit::instance 方法会调用该方法进行设置:
  1.       if (ACE_Thread_Exit::is_constructed_ == 0)
  2.         {
  3.           ACE_NEW_RETURN (instance_,
  4.                           ACE_TSS_TYPE (ACE_Thread_Exit),
  5.                           0);
  6.           ACE_Thread_Exit::is_constructed_ = 1;
  7.           ACE_Thread_Manager::set_thr_exit (instance_);
  8.         }
复制代码
       而 ACE_Thread_Exit::instance 又只有在 ACE_Thread_Adapter::invoke 中被调用:
  1.   ACE_Thread_Exit *exit_hook_instance = ACE_Thread_Exit::instance ();
复制代码
       该函数之前见过,它就是所有 ACE_Thread_Manager 创建的线程的回调函数,也就是说只要使用 ACE_Thread_Manager 创建了线程,这个对象也就会随之一起创建。关键是看下这个对象的实际类型:
  1. ACE_TSS <ACE_Thread_Exit>
复制代码
       ACE_TSS 是 ACE 用来简化 TSS 操作的模板类,使用它的 operator-> 操作可以直接访问位于线程专用数据中的对象。关键在于它在背后使用了 thr_keycreate 方法,会将一个清理函数注册给 TSS 机制:
  1.       if (ACE_Thread::keycreate (ACE_const_cast (ACE_thread_key_t *, &this->key_),
  2. #if defined (ACE_HAS_THR_C_DEST)
  3.                                  &ACE_TSS_C_cleanup,
  4. #else
  5.                                  &ACE_TSS<TYPE>::cleanup,
  6. #endif /* ACE_HAS_THR_C_DEST */
  7.                                  (void *) this) != 0)
复制代码
       也就是说线程退出时,ACE_TSS<ACE_Thread_Exit>::cleanup 清理函数会被调用,这又是什么东东:
  1. template <class TYPE> void
  2. ACE_TSS<TYPE>::cleanup (void *ptr)
  3. {
  4.   // Cast this to the concrete TYPE * so the destructor gets called.
  5.   delete (TYPE *) ptr;
  6. }
复制代码
       直接明了,销毁对象,看下 ACE_Thread_Exit 的析构函数做了什么:
  1. ACE_Thread_Exit::~ACE_Thread_Exit (void)
  2. {
  3.   ACE_OS_TRACE ("ACE_Thread_Exit::~ACE_Thread_Exit");
  4. }
复制代码
       居然是空的,怎么可能 ?! 原来它还包含一个名为 ACE_Thread_Control 的内嵌对象:
  1.   /// Automatically add/remove the thread from the
  2.   /// <ACE_Thread_Manager>.
  3.   ACE_Thread_Control thread_control_;
复制代码
       那么它的析构函数是什么呢?
  1. ACE_Thread_Control::~ACE_Thread_Control (void)
  2. {
  3.   ACE_OS_TRACE ("ACE_Thread_Control::~ACE_Thread_Control");
  4. #if defined (ACE_HAS_RECURSIVE_THR_EXIT_SEMANTICS) || defined (ACE_HAS_TSS_EMULATION) || defined (ACE_WIN32)
  5.   this->exit (this->status_, 0);
  6. #else
  7.   this->exit (this->status_, 1);
  8. #endif /* ACE_HAS_RECURSIVE_THR_EXIT_SEMANTICS */
  9. }
复制代码
       真是踏破铁鞋无觅处,这里,它调用了 ACE_Thread_Control::exit:
  1. ACE_Thread_Control::exit (ACE_THR_FUNC_RETURN exit_status, int do_thr_exit)
  2. {
  3.   ACE_OS_TRACE ("ACE_Thread_Control::exit");
  4.   if (this->tm_ != 0)
  5.     return this->tm_->exit (exit_status, do_thr_exit);
  6.   else
  7.     {
  8. #if !defined (ACE_HAS_TSS_EMULATION)
  9.       // With ACE_HAS_TSS_EMULATION, we let ACE_Thread_Adapter::invoke ()
  10.       // exit the thread after cleaning up TSS.
  11.       ACE_OS::thr_exit (exit_status);
  12. #endif /* ! ACE_HAS_TSS_EMULATION */
  13.       return 0;
  14.     }
  15. }
复制代码
       后者又回调了 ACE_Thread_Manager 的 exit 方法:
  1.   {
  2.     ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, 0));
  3.     // Find the thread id, but don't use the cache.  It might have been
  4.     // deleted already.
  5. #if defined (VXWORKS)
  6.     ACE_hthread_t id;
  7.     ACE_OS::thr_self (id);
  8.     ACE_Thread_Descriptor* td = this->find_hthread (id);
  9. #else  /* ! VXWORKS */
  10.     ACE_thread_t id = ACE_OS::thr_self ();
  11.     ACE_Thread_Descriptor* td = this->find_thread (id);
  12. #endif /* ! VXWORKS */
  13.     if (td != 0)
  14.      {
  15.        // @@ We call Thread_Descriptor terminate this realize the cleanup
  16.        // process itself.
  17.        td->terminate();
  18.      }
  19.   }
复制代码
       这个函数里最重要的工作就是调用线程描述符 ACE_Thread_Descriptor 之 terminate,在该方法中,除了将自己从 Thread_Manager 表中移除,它将之前程序注册在本线程中的清理函数一一回调,测试程序的清理函数就是在这个时候调用的。由于 clean_tss 在线程调用 thr_exit 时也会被调用,所以基于 TSS 清理机制的这套线程退出机制也会起作用。打开 USE_EXIT 编译开关,重新编译并运行,输出如下:
  1. (6300) main start
  2. (2148) start
  3. (2148) exit
  4. (2148) clean up 0x0 (42).
  5. (6300) main end
  6. 请按任意键继续. . .
复制代码
       可以看到清理函数依然被调用了。
        至此,对 ACE TSS 清理机制的分析及应用就都介绍完了,感兴趣的读者可以自己试一下这些例子,或者在实际中直接使用 ACE 的这一功能。

 楼主| 发表于 2014-3-19 15:58:57 | 显示全部楼层
人都哪去了……
发表于 2014-6-29 13:05:18 | 显示全部楼层
版主好文章!每月出一篇啊!
您需要登录后才可以回帖 登录 | 用户注册

本版积分规则

Archiver|手机版|小黑屋|ACE Developer ( 京ICP备06055248号 )

GMT+8, 2024-12-22 11:25 , Processed in 0.018064 second(s), 5 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表