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