作者:陈曦
日期:2012-8-5 16:13:36
环境:[Mac 10.7.1 Lion Intel i3 支持64位指令 gcc4.2.1 xcode4.2 苹果开源代码Libc-763.11]
转载请注明出处
每日总结:优秀的架构都是类似的,垃圾的架构一般都是一个原因:代码内部原理掌握得不够
Q1: main函数中最后用return 0; 和使用 exit(0); 退出进程到底有什么不同?
A: 一种很简单的区别方式就是return 0是返回给调用函数者,而exit(0)是直接返回给系统。但是,前者返回给什么函数?写如下代码:
- int main(int argc, char **argv)
- {
- return 0;
- }
- #include <stdlib.h>
- int main(int argc, char **argv)
- {
- exit(0);
- }
分别编译成可执行文件。使用MachOView工具查看各自的可执行文件:
第一个对应的_start和_main汇编为:
首先要确定,程序的入口点默认是_start. 同时可以看到,main中最后只是简单的返回给调用者;但是start中首先调用了main,然后调用了exit,这说明了虽然main函数最后没做什么退出进程的事情,返回到start后依然会退出进程。
再看第二个:
可以看到,main最后调用了exit函数,start最后依然也调用了exit,但是start最后调用的exit已经无法运行到,因为main调用后进程就结束了。
由上面两种情况可以看出,main函数最后调用return 0; 或者 exit(0); 可以说是基本一样,除了程序流程有点变化,没有什么大的区别。
Q2: 主线程最后调用pthread_exit来退出可行吗?
A: 如果仅仅从退出主线程的角度来考虑,这是可行的; 但是,一般来说,进程应该总是主线程最后退出,这样比较符合编程的基本原则;如果这样的话,主线程使用pthread_exit退出就可能出问题: 因为,它虽然会释放已经注册的清理函数以及线程特有的数据,但是它并不一定会释放进程相关的资源(包括内存、信号量、互斥体等)或者执行进程注册的退出函数(如atexit注册的退出函数),这样就很有可能导致内存泄露。当然,上面说并不一定,是因为,如果主线程是进程中最后退出的线程,那么进程相关的资源和进程注册的退出函数才会被执行。为了明白pthread_exit内部调用关系,首先,我们下载苹果开源代码Libc-763.11,这是对应mac系统10.7.1的开源libc代码,如果是其它系统,请下载对应版本的代码。苹果开源代码网站: http://opensource.apple.com/
首先找到pthread_exit的实现代码:
- void
- pthread_exit(void *value_ptr)
- {
- pthread_t self = pthread_self();
- /* if the current thread is a workqueue thread, just crash the app, as per libdispatch folks */
- if (self->wqthread == 0) {
- _pthread_exit(self, value_ptr);
- } else {
- LIBC_ABORT("pthread_exit() may only be called against threads created via pthread_create()");
- }
- }
进入主要调用部分_pthread_exit函数:
- static void
- _pthread_exit(pthread_t self, void *value_ptr)
- {
- struct __darwin_pthread_handler_rec *handler;
- kern_return_t kern_res;
- int thread_count;
- int newstyle = self->newstyle;
- /* Make this thread not to receive any signals */
- __disable_threadsignal(1);
- #if PTH_TRACE
- __kdebug_trace(0x900001c, self, newstyle, 0, 0, 0);
- #endif
- /* set cancel state to disable and type to deferred */
- _pthread_setcancelstate_exit(self, value_ptr, __unix_conforming);
- while ((handler = self->__cleanup_stack) != 0)
- {
- (handler->__routine)(handler->__arg); // call cleanup handler
- self->__cleanup_stack = handler->__next;
- }
- _pthread_tsd_cleanup(self); // Clean up thread specific data
- if (newstyle == 0) {
- _pthread_reap_threads();
- LOCK(self->lock);
- self->detached |= _PTHREAD_EXITED;
- if (self->detached & PTHREAD_CREATE_JOINABLE) {
- mach_port_t death = self->death;
- self->exit_value = value_ptr;
- UNLOCK(self->lock);
- /* the joiner will need a kernel thread reference, leave ours for it */
- if (death) {
- PTHREAD_MACH_CALL(semaphore_signal(death), kern_res);
- if (kern_res != KERN_SUCCESS)
- fprintf(stderr,
- "semaphore_signal(death) failed: %s\n",
- mach_error_string(kern_res));
- }
- LOCK(_pthread_list_lock);
- thread_count = --_pthread_count;
- UNLOCK(_pthread_list_lock);
- } else {
- UNLOCK(self->lock);
- LOCK(_pthread_list_lock);
- TAILQ_REMOVE(&__pthread_head, self, plist);
- #if PTH_LISTTRACE
- __kdebug_trace(0x9000010, self, 0, 0, 5, 0);
- #endif
- thread_count = --_pthread_count;
- UNLOCK(_pthread_list_lock);
- /* with no joiner, we let become available consume our cached ref */
- _pthread_become_available(self, self->kernel_thread);
- }
- if (thread_count <= 0) // if the thread is the last one, then exit the process
- exit(0);
- /* Use a new reference to terminate ourselves. Should never return. */
- // internal terminate thread
- PTHREAD_MACH_CALL(thread_terminate(mach_thread_self()), kern_res);
- fprintf(stderr, "thread_terminate(mach_thread_self()) failed: %s\n",
- mach_error_string(kern_res));
- } else { // newstyle != 0
- semaphore_t joinsem = SEMAPHORE_NULL;
- if ((self->joiner_notify == (mach_port_t)0) && (self->detached & PTHREAD_CREATE_JOINABLE))
- joinsem = new_sem_from_pool();
- LOCK(self->lock);
- self->detached |= _PTHREAD_EXITED;
- self->exit_value = value_ptr;
- if (self->detached & PTHREAD_CREATE_JOINABLE) {
- if (self->joiner_notify == (mach_port_t)0) {
- self->joiner_notify = joinsem;
- joinsem = SEMAPHORE_NULL;
- }
- UNLOCK(self->lock);
- if (joinsem != SEMAPHORE_NULL)
- restore_sem_to_pool(joinsem);
- _pthread_free_pthread_onstack(self, 0, 1);
- } else {
- UNLOCK(self->lock);
- /* with no joiner, we let become available consume our cached ref */
- if (joinsem != SEMAPHORE_NULL)
- restore_sem_to_pool(joinsem);
- _pthread_free_pthread_onstack(self, 1, 1);
- }
- }
- LIBC_ABORT("thread %p didn't exit", self);
- }
为了更直观,我在上面的代码中加入了部分注释。从这里可以看出,pthread_exit做或不做如下几件事情:
1、已经注册的清理事件被执行;
2、线程私有数据被清理;
3、如果此线程是最后一个线程,那么会调用exit(0)退出进程。
4、不会释放进程相关的资源(如文件句柄、互斥体、内存等), 也不会执行进程注册的退出函数(如atexit注册的函数); 如果是最后一个线程,那么调用exit(0)将会做释放进程资源和执行进程注册的退出函数的事情。
由上面描述,pthread_exit还是一个比较安全的退出函数,但是主线程使用的话不太推荐,子线程使用还是没问题的。
Q3: 举个子线程跑完,主线程最后执行调用pthread_exit,而能调用进程资源释放的例子吧。
A:
- #include <stdio.h>
- #include <stdlib.h>
- #include <pthread.h>
- #include <unistd.h>
- #include <time.h>
- #include <errno.h>
- #include <string.h>
- #define PRINT_D(intValue) printf(#intValue" is %d\n", (intValue));
- #define PRINT_U(uintValue) printf(#uintValue" is %lu\n", (uintValue));
- #define PRINT_STR(str) printf(#str" is %s\n", (str));
- #define PRINT_S(str) printf("%s\n", (#str));
- #define FOREVER_PRINT { while(1) printf("...");}
- #define RETURN_ERROR(func, ret, return_value) \
- if((ret) != 0) \
- { \
- perror(#func" error"); \
- printf("ret is %d\n", (ret)); \
- return (return_value); \
- }
- void *thread_func(void *args)
- {
- printf("son thread end...\n");
- return NULL;
- }
- void exit_process()
- {
- PRINT_S("exit_process is called...")
- }
- // main thread
- int main(int argc, char **argv)
- {
- pthread_t son_thread;
- int ret;
- atexit(exit_process);
- ret = pthread_create(&son_thread, NULL, thread_func, NULL);
- RETURN_ERROR(pthread_create, ret, -1)
- ret = pthread_detach(son_thread);
- RETURN_ERROR(pthread_detach, ret, -1)
- sleep(2);
- pthread_exit(NULL);
- printf("[Main thread]: end...\n");
- return 0;
- }
主线程创建子线程后,睡眠2秒等待子线程完成,然后调用pthread_exit退出主线程。
执行结果:
- son thread end...
- "exit_process is called..."
进程注册的exit_process函数被执行了,这是因为子线程正常结束了,主线程是最后一个退出的线程,所以它调用pthread_exit退出将导致进程资源和进程注册的退出函数的执行; 但是,此时主线程已经无力继续执行了,所以主线程最后的一句printf语句无法被执行了。
Q4: 如果子线程是最后退出的线程,子线程是否同样可以调用注册的清理函数以及线程私有数据?
A: 由上面的理论,当然可以。下面将实例证明此过程:
- #define PRINT_S(str) printf("%s\n", (#str));
- #define LOG_ENTER_FUNC printf("enter func %s\n", __func__);
- pthread_key_t key;
- void exit_process_sonthread_registered()
- {
- LOG_ENTER_FUNC
- }
- void exit_process()
- {
- int ret;
- LOG_ENTER_FUNC
- ret = pthread_key_delete(key);
- if(ret != 0)
- perror("pthread_key_delete error");
- else
- PRINT_S("pthread_key_delete ok");
- }
- void cleanup_func(void *arg)
- {
- LOG_ENTER_FUNC
- }
- void destruct_key_func(void *arg)
- {
- LOG_ENTER_FUNC
- }
- void *thread_func(void *args)
- {
- int temp = 99;
- int ret;
- pthread_cleanup_push(cleanup_func, NULL);
- atexit(exit_process_sonthread_registered);
- sleep(2);
- ret = pthread_setspecific(key, &temp); // use key
- if(ret != 0)
- perror("pthread_setspecific error");
- printf("son thread end...\n");
- pthread_exit(NULL);
- pthread_cleanup_pop(0);
- }
- // main thread
- int main(int argc, char **argv)
- {
- pthread_t son_thread;
- int ret;
- atexit(exit_process);
- ret = pthread_key_create(&key, destruct_key_func);
- RETURN_ERROR(pthread_key_create, ret, -1)
- ret = pthread_create(&son_thread, NULL, thread_func, NULL);
- RETURN_ERROR(pthread_create, ret, -1)
- ret = pthread_detach(son_thread);
- RETURN_ERROR(pthread_detach, ret, -1)
- printf("[Main thread]: will end...\n");
- pthread_exit(NULL);
- return 0;
- }
上面的代码,主线程自己结束后,子线程继续执行,并且子线程有注册的清理函数,有使用线程私有数据,下面是运行结果:
- [Main thread]: will end...
- son thread end...
- enter func cleanup_func
- enter func destruct_key_func
- enter func exit_process_sonthread_registered
- enter func exit_process
- "pthread_key_delete ok"
可以看出,该执行的都执行了。
Q5: 上面的过程,子线程虽然睡眠了2秒,但是如何证明主线程确实比子线程先执行完了?
A: 可以使用pthread_kill函数来得到线程的活跃状态。如下代码:
- pthread_t main_thread;
- void *thread_func(void *args)
- {
- int ret;
- sleep(2);
- // test whether main thread is active
- ret = pthread_kill(main_thread, 0);
- if(ret == ESRCH)
- printf("main thread has quited or not exist...\n");
- else if(ret == EINVAL)
- printf("signal isn't valid...\n");
- else
- printf("main thread is active...\n");
- printf("son thread end...\n");
- pthread_exit(NULL);
- }
- // main thread
- int main(int argc, char **argv)
- {
- pthread_t son_thread;
- int ret;
- main_thread = pthread_self(); // get main thread
- ret = pthread_create(&son_thread, NULL, thread_func, NULL);
- RETURN_ERROR(pthread_create, ret, -1)
- ret = pthread_detach(son_thread);
- RETURN_ERROR(pthread_detach, ret, -1)
- printf("[Main thread]: will end...\n");
- pthread_exit(NULL);
- return 0;
- }
执行结果:
- [Main thread]: will end...
- main thread has quited or not exist...
- son thread end...
可以看出,子线程使用pthread_kill得到主线程的状态为退出或者不存在,其实这也证明了主线程已经提前退出了。
Q6: pthread_kill函数是做什么的?
A: 不要被它的名字吓怕了,它不是专门kill线程的,它只是给线程传信号的。如果信号传递的是0,那么只执行线程是否存在的检查,就像上面的代码那样。但是,如果传入的信号不是0,而是其它信号(或者说是进程信号,如SIGINT, SIGQUIT等),那么线程将需要处理这些信号。如果线程没有注册这些信号的处理函数,那么信号将默认继续传送给进程。也就是说,如果给一个线程传递SIGQUIT信号,但是线程没有注册处理SIGQUIT信号的函数,那么SIGQUIT信号被传递到进程中,进程会结束。
下面看下pthread_kill的源代码:
- int
- pthread_kill (
- pthread_t th,
- int sig)
- {
- int error = 0;
- mach_port_t kport = MACH_PORT_NULL;
- if ((sig < 0) || (sig > NSIG))
- return(EINVAL);
- if (_pthread_lookup_thread(th, &kport, 0) != 0)
- return (ESRCH); /* Not a valid thread */
- /* if the thread is a workqueue thread, just return error */
- if ((th->wqthread != 0) && (th->wqkillset ==0)) {
- return(ENOTSUP);
- }
- error = __pthread_kill(kport, sig);
- if (error == -1)
- error = errno;
- return(error);
- }
Q7: 对于一个线程A,它创建了子线程B,A结束了,B会结束吗?
A: 可以这么说,一个线程中创建了另一个线程,此时它们没有任何依赖关系的,它们都根据操作系统的调度系统自由运行,该结束就结束,该不结束就不结束。下面有个例子:
- void *grandson_thread_func(void *arg)
- {
- sleep(2);
- printf("grandson_thread_func\n");
- return NULL;
- }
- void *thread_func(void *arg)
- {
- pthread_t grandson_thread;
- int ret;
- ret = pthread_create(&grandson_thread, NULL, grandson_thread_func, NULL);
- RETURN_ERROR(pthread_create, ret, 0)
- ret = pthread_detach(grandson_thread);
- RETURN_ERROR(pthread_detach, ret, 0)
- PRINT_S("son thread will exit")
- return NULL;
- }
- // main thread
- int main(int argc, char **argv)
- {
- pthread_t son_thread;
- int ret;
- ret = pthread_create(&son_thread, NULL, thread_func, NULL);
- RETURN_ERROR(pthread_create, ret, -1)
- ret = pthread_detach(son_thread);
- RETURN_ERROR(pthread_detach, ret, -1)
- while (1) // wait here
- ;
- printf("[Main thread]: will end...\n");
- return 0;
- }
上面的代码中,主线程创建新线程A,新线程A创建一个新线程B,线程A结束后,如果线程B中的输出依然可以输出,表明A的结束并没有导致B的结束。
运行结果:
- "son thread will exit"
- grandson_thread_func
可以看到,线程B没有因为创建它的线程A的结束而结束。
Q8: 为什么主线程最后结束后,所有子线程不管运行不运行,都会被中断结束?
A: 这在于主线程执行函数比较特殊,它有_start模块调用,它结束后会调用exit结束进程,这个操作将强制终止进程所有子线程的运行,导致了子线程被中断。而对于子线程来说,就没有这种特殊情况。
Q9: pthread_cancel同样可以取消线程执行,为什么下面代码中的线程没有很快结束?
- pthread_key_t key;
- int i = 0;
- void destructkey_func(void *arg)
- {
- LOG_ENTER_FUNC
- }
- void cleanup_func(void *arg)
- {
- LOG_ENTER_FUNC
- PRINT_D(i)
- }
- void *thread_func(void *arg)
- {
- int sum;
- int ret;
- pthread_cleanup_push(cleanup_func, NULL);
- ret = pthread_setspecific(key, &i);
- RETURN_ERROR(pthread_setspecific, ret, 0)
- for(; i < INT_MAX; ++i)
- sum += i;
- PRINT_S("son thread will exit")
- return NULL;
- pthread_cleanup_pop(0);
- }
- // main thread
- int main(int argc, char **argv)
- {
- pthread_t son_thread;
- int ret;
- ret = pthread_key_create(&key, destructkey_func);
- RETURN_ERROR(pthread_key_create, ret, -1)
- ret = pthread_create(&son_thread, NULL, thread_func, NULL);
- RETURN_ERROR(pthread_create, ret, -1)
- // cancel the son thread
- ret = pthread_cancel(son_thread);
- RETURN_ERROR(pthread_cancel, ret, -1)
- // wait son thread to exit
- ret = pthread_join(son_thread, NULL);
- RETURN_ERROR(pthread_join, ret, -1)
- ret = pthread_key_delete(key);
- RETURN_ERROR(pthread_key_delete, ret, -1)
- printf("[Main thread]: will end...\n");
- return 0;
- }
主线程创建子线程后,cancel它,但是子线程在for循环中一直无法脱身进入结束状态,这是为什么?
A: 这在于pthread_cancel只是在线程中设置了一个取消的状态,至于线程会不会进入结束状态,这取决于线程是否允许被取消,以及被取消的类型是立即还是延迟的。延迟的就是指需要进入取消点才会进入结束状态, 立即的是指尽快结束线程(但是这也不一定能保证线程一定能满足需求一样尽快结束,内部实现可能依然会有一些等待时间)。上面的代码运行:
- [xxxx seconds later]
- "son thread will exit"
- enter func cleanup_func
- i is 2147483647
- enter func destructkey_func
- [Main thread]: will end...
第一行不是运行的输出,而是指明是N秒过后,才输出后面的信息。在这里,可以看出,cancel以一个线程,线程的清理函数以及线程私有数据也是会被调用或者释放的。从这个角度来说,pthread_cancel也是一个相对不错的结束线程的方式。
在上面的那段代码中,线程的取消状态和类型没有被特定的设置,那么是采用默认的状态,那就是允许取消以及采用延迟取消的方式.延迟取消的方式就是必须线程进入取消点的时候才会进入结束。取消点在c库中并不能保证很好地实现,但是pthread库中,根据标准,pthread_join, pthread_testcancel, pthread_cond_wait, pthread_cond_timedwait, sem_wait, sigwait函数等引起阻塞的函数都是取消点。另外,read, write等引起系统阻塞的系统调用也是取消点。从此,可以看出,上面的子线程代码for循环一直处于内存数据操作,没有和上面所说的取消点有关系,所以线程会一直运行到最后的PRINT_S输出代码才引起取消点有效,线程进入结束状态。
了解了上面的原理之后,就可以让线程尽可能快地随着pthread_cancel调用而结束了,如下:
- void *thread_func(void *arg)
- {
- int sum;
- int ret;
- pthread_cleanup_push(cleanup_func, NULL);
- ret = pthread_setspecific(key, &i);
- RETURN_ERROR(pthread_setspecific, ret, 0)
- for(; i < INT_MAX; ++i)
- {
- // if cancel mark exists, then will exit the thread as soon as possible
- pthread_testcancel();
- sum += i;
- }
- PRINT_S("son thread will exit")
- return NULL;
- pthread_cleanup_pop(0);
- }
上面对子线程代码的修改为for循环中加入了pthread_testcancel函数,它会判断是否取消标志被标示,如果被标示,那么线程将立即准备结束。如下运行结果:
- enter func cleanup_func
- i is 0
- enter func destructkey_func
- [Main thread]: will end...
可以看出,for循环最多只跑1遍,线程就结束了;可见pthread_testcancel作用之大!
Q10: 如果采用设置线程取消类型为PTHREAD_CANCEL_ASYNCHRONOUS的方式来尽快可以结束线程,这样线程一定会立即结束吗?
A: 这个不一定。设置这个标志位只保证了会尽快来结束线程,但是调度策略不同,以及线程执行情况不同,都无法保证。如下:
- void *thread_func(void *arg)
- {
- int sum;
- int ret;
- int old_type;
- pthread_cleanup_push(cleanup_func, NULL);
- ret = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old_type);
- RETURN_ERROR(pthread_setcanceltype, ret, 0)
- ret = pthread_setspecific(key, &i);
- RETURN_ERROR(pthread_setspecific, ret, 0)
- for(; i < INT_MAX; ++i)
- {
- sum += i;
- }
- PRINT_S("son thread will exit")
- return NULL;
- pthread_cleanup_pop(0);
- }
- // main thread
- int main(int argc, char **argv)
- {
- pthread_t son_thread;
- int ret;
- ret = pthread_key_create(&key, destructkey_func);
- RETURN_ERROR(pthread_key_create, ret, -1)
- ret = pthread_create(&son_thread, NULL, thread_func, NULL);
- RETURN_ERROR(pthread_create, ret, -1)
- sleep(1);
- // cancel the son thread
- ret = pthread_cancel(son_thread);
- RETURN_ERROR(pthread_cancel, ret, -1)
- PRINT_S("has call cancel the son thread:")
- // wait son thread to exit
- ret = pthread_join(son_thread, NULL);
- RETURN_ERROR(pthread_join, ret, -1)
- ret = pthread_key_delete(key);
- RETURN_ERROR(pthread_key_delete, ret, -1)
- printf("[Main thread]: will end...\n");
- return 0;
- }
运行结果:
- "has call cancel the son thread:"
- [xxxxx seconds later]
- "son thread will exit"
- enter func cleanup_func
- i is 2147483647
- enter func destructkey_func
- [Main thread]: will end...
可以看出,线程还是等待for循环结束才结束。
Q11: 对于pthread_cancel函数,它内部是如何实现的?
A: 如下代码:
- int
- pthread_cancel(pthread_t thread)
- {
- #if __DARWIN_UNIX03
- if (__unix_conforming == 0)
- __unix_conforming = 1;
- #endif /* __DARWIN_UNIX03 */
- if (_pthread_lookup_thread(thread, NULL, 0) != 0)
- return(ESRCH);
- /* if the thread is a workqueue thread, then return error */
- if (thread->wqthread != 0) {
- return(ENOTSUP);
- }
- #if __DARWIN_UNIX03
- int state;
- LOCK(thread->lock);
- state = thread->cancel_state |= _PTHREAD_CANCEL_PENDING;
- UNLOCK(thread->lock);
- if (state & PTHREAD_CANCEL_ENABLE)
- __pthread_markcancel(thread->kernel_thread); // do cancel mark
- #else /* __DARWIN_UNIX03 */
- thread->cancel_state |= _PTHREAD_CANCEL_PENDING;
- #endif /* __DARWIN_UNIX03 */
- return (0);
- }
可以很清楚地看到,它内部设置了cancel标志位。对于__pthread_markcancel函数,苹果没有公开代码,只有一个原型:
- extern int __pthread_markcancel(int);
另外,我们看下pthread_setcancelstate的源代码:
- /*
- * Query/update the cancelability 'state' of a thread
- */
- int
- pthread_setcancelstate(int state, int *oldstate)
- {
- #if __DARWIN_UNIX03
- if (__unix_conforming == 0) {
- __unix_conforming = 1;
- }
- return (_pthread_setcancelstate_internal(state, oldstate, 1));
- #else /* __DARWIN_UNIX03 */
- return (_pthread_setcancelstate_internal(state, oldstate, 0));
- #endif /* __DARWIN_UNIX03 */
- }
- /*
- * Query/update the cancelability 'state' of a thread
- */
- int
- _pthread_setcancelstate_internal(int state, int *oldstate, int conforming)
- {
- pthread_t self = pthread_self();
- switch (state) {
- case PTHREAD_CANCEL_ENABLE:
- if (conforming)
- __pthread_canceled(1);
- break;
- case PTHREAD_CANCEL_DISABLE:
- if (conforming)
- __pthread_canceled(2);
- break;
- default:
- return EINVAL;
- }
- self = pthread_self();
- LOCK(self->lock);
- if (oldstate)
- *oldstate = self->cancel_state & _PTHREAD_CANCEL_STATE_MASK;
- self->cancel_state &= ~_PTHREAD_CANCEL_STATE_MASK;
- self->cancel_state |= state;
- UNLOCK(self->lock);
- if (!conforming)
- _pthread_testcancel(self, 0); /* See if we need to 'die' now... */
- return (0);
- }
它的主要作用是设置cancel_state标志位; 同时,它对于是否允许取消线程也做了不同的处理。
对于pthread_setcanceltype函数,代码如下:
- /*
- * Query/update the cancelability 'type' of a thread
- */
- int
- pthread_setcanceltype(int type, int *oldtype)
- {
- pthread_t self = pthread_self();
- #if __DARWIN_UNIX03
- if (__unix_conforming == 0)
- __unix_conforming = 1;
- #endif /* __DARWIN_UNIX03 */
- if ((type != PTHREAD_CANCEL_DEFERRED) &&
- (type != PTHREAD_CANCEL_ASYNCHRONOUS))
- return EINVAL;
- self = pthread_self();
- LOCK(self->lock);
- if (oldtype)
- *oldtype = self->cancel_state & _PTHREAD_CANCEL_TYPE_MASK;
- self->cancel_state &= ~_PTHREAD_CANCEL_TYPE_MASK;
- self->cancel_state |= type;
- UNLOCK(self->lock);
- #if !__DARWIN_UNIX03
- _pthread_testcancel(self, 0); /* See if we need to 'die' now... */
- #endif /* __DARWIN_UNIX03 */
- return (0);
- }
pthread_testcancel函数做了如下:
- void
- pthread_testcancel(void)
- {
- pthread_t self = pthread_self();
- #if __DARWIN_UNIX03
- if (__unix_conforming == 0)
- __unix_conforming = 1;
- _pthread_testcancel(self, 1);
- #else /* __DARWIN_UNIX03 */
- _pthread_testcancel(self, 0);
- #endif /* __DARWIN_UNIX03 */
- }
- /*
- * Insert a cancellation point in a thread.
- */
- __private_extern__ void
- _pthread_testcancel(pthread_t thread, int isconforming)
- {
- LOCK(thread->lock);
- if ((thread->cancel_state & (PTHREAD_CANCEL_ENABLE|_PTHREAD_CANCEL_PENDING)) ==
- (PTHREAD_CANCEL_ENABLE|_PTHREAD_CANCEL_PENDING))
- {
- UNLOCK(thread->lock);
- if (isconforming)
- pthread_exit(PTHREAD_CANCELED);
- else
- pthread_exit(0);
- }
- UNLOCK(thread->lock);
- }
可以看出,它在必要的时刻,调用了pthread_exit来结束线程。
最后,顺便贴下pthread_create的源代码(因为调用函数太多,只贴出前2个级别调用):
- int
- pthread_create(pthread_t *thread,
- const pthread_attr_t *attr,
- void *(*start_routine)(void *),
- void *arg)
- {
- return _new_pthread_create_suspended(thread, attr, start_routine, arg, 0);
- }
内部函数调用:
- static int
- _new_pthread_create_suspended(pthread_t *thread,
- const pthread_attr_t *attr,
- void *(*start_routine)(void *),
- void *arg,
- int create_susp)
- {
- pthread_attr_t *attrs;
- void *stack;
- int error;
- unsigned int flags;
- pthread_t t,t2;
- kern_return_t kern_res;
- mach_port_t kernel_thread = MACH_PORT_NULL;
- int needresume;
- task_t self = mach_task_self();
- int kernalloc = 0;
- int susp = create_susp;
- if ((attrs = (pthread_attr_t *)attr) == (pthread_attr_t *)NULL)
- { /* Set up default paramters */
- attrs = &_pthread_attr_default;
- } else if (attrs->sig != _PTHREAD_ATTR_SIG) {
- return EINVAL;
- }
- error = 0;
- if (((attrs->policy != _PTHREAD_DEFAULT_POLICY) ||
- (attrs->param.sched_priority != default_priority)) && (create_susp == 0)) {
- needresume = 1;
- susp = 1;
- } else
- needresume = 0;
- /* In default policy (ie SCHED_OTHER) only sched_priority is used. Check for
- * any change in priority or policy is needed here.
- */
- if ((__oldstyle == 1) || (create_susp != 0)) {
- /* Rosetta or pthread_create_suspended() */
- /* running under rosetta */
- /* Allocate a stack for the thread */
- #if PTH_TRACE
- __kdebug_trace(0x9000000, create_susp, 0, 0, 0, 0);
- #endif
- if ((error = _pthread_allocate_stack(attrs, &stack)) != 0) {
- return(error);
- }
- t = (pthread_t)malloc(sizeof(struct _pthread));
- *thread = t;
- if (susp) {
- /* Create the Mach thread for this thread */
- PTHREAD_MACH_CALL(thread_create(self, &kernel_thread), kern_res);
- if (kern_res != KERN_SUCCESS)
- {
- printf("Can't create thread: %d\n", kern_res);
- return(EINVAL);
- }
- }
- if ((error = _pthread_create(t, attrs, stack, kernel_thread)) != 0)
- {
- return(error);
- }
- set_malloc_singlethreaded(0);
- __is_threaded = 1;
- /* Send it on it's way */
- t->arg = arg;
- t->fun = start_routine;
- t->newstyle = 0;
- /* Now set it up to execute */
- LOCK(_pthread_list_lock);
- TAILQ_INSERT_TAIL(&__pthread_head, t, plist);
- #if PTH_LISTTRACE
- __kdebug_trace(0x900000c, t, 0, 0, 4, 0);
- #endif
- _pthread_count++;
- UNLOCK(_pthread_list_lock);
- _pthread_setup(t, _pthread_body, stack, susp, needresume);
- return(0);
- } else {
- flags = 0;
- if (attrs->fastpath == 1)
- kernalloc = 1;
- if (attrs->detached == PTHREAD_CREATE_DETACHED)
- flags |= PTHREAD_START_DETACHED;
- if (attrs->schedset != 0) {
- flags |= PTHREAD_START_SETSCHED;
- flags |= ((attrs->policy & PTHREAD_START_POLICY_MASK) << PTHREAD_START_POLICY_BITSHIFT);
- flags |= (attrs->param.sched_priority & PTHREAD_START_IMPORTANCE_MASK);
- }
- set_malloc_singlethreaded(0);
- __is_threaded = 1;
- if (kernalloc == 0) {
- /* Allocate a stack for the thread */
- flags |= PTHREAD_START_CUSTOM;
- if ((error = _pthread_create_pthread_onstack(attrs, &stack, &t)) != 0) {
- return(error);
- }
- /* Send it on it's way */
- t->arg = arg;
- t->fun = start_routine;
- t->newstyle = 1;
- #if PTH_TRACE
- __kdebug_trace(0x9000004, t, flags, 0, 0, 0);
- #endif
- if ((t2 = __bsdthread_create(start_routine, arg, stack, t, flags)) == (pthread_t)-1) {
- _pthread_free_pthread_onstack(t, 1, 0);
- return (EAGAIN);
- }
- else t=t2;
- LOCK(_pthread_list_lock);
- t->parentcheck = 1;
- if ((t->childexit != 0) && ((t->detached & PTHREAD_CREATE_DETACHED) == PTHREAD_CREATE_DETACHED)) {
- /* detached child exited, mop up */
- UNLOCK(_pthread_list_lock);
- #if PTH_TRACE
- __kdebug_trace(0x9000008, t, 0, 0, 1, 0);
- #endif
- if(t->freeStackOnExit)
- vm_deallocate(self, (mach_vm_address_t)(uintptr_t)t, pthreadsize);
- else
- free(t);
- } else if (t->childrun == 0) {
- TAILQ_INSERT_TAIL(&__pthread_head, t, plist);
- _pthread_count++;
- #if PTH_LISTTRACE
- __kdebug_trace(0x900000c, t, 0, 0, 1, 0);
- #endif
- UNLOCK(_pthread_list_lock);
- } else
- UNLOCK(_pthread_list_lock);
- *thread = t;
- #if PTH_TRACE
- __kdebug_trace(0x9000014, t, 0, 0, 1, 0);
- #endif
- return (0);
- } else {
- /* kernel allocation */
- #if PTH_TRACE
- __kdebug_trace(0x9000018, flags, 0, 0, 0, 0);
- #endif
- if ((t = __bsdthread_create(start_routine, arg, (void *)attrs->stacksize, NULL, flags)) == (pthread_t)-1)
- return (EAGAIN);
- /* Now set it up to execute */
- LOCK(_pthread_list_lock);
- t->parentcheck = 1;
- if ((t->childexit != 0) && ((t->detached & PTHREAD_CREATE_DETACHED) == PTHREAD_CREATE_DETACHED)) {
- /* detached child exited, mop up */
- UNLOCK(_pthread_list_lock);
- #if PTH_TRACE
- __kdebug_trace(0x9000008, t, pthreadsize, 0, 2, 0);
- #endif
- vm_deallocate(self, (mach_vm_address_t)(uintptr_t)t, pthreadsize);
- } else if (t->childrun == 0) {
- TAILQ_INSERT_TAIL(&__pthread_head, t, plist);
- _pthread_count++;
- #if PTH_LISTTRACE
- __kdebug_trace(0x900000c, t, 0, 0, 2, 0);
- #endif
- UNLOCK(_pthread_list_lock);
- } else
- UNLOCK(_pthread_list_lock);
- *thread = t;
- #if PTH_TRACE
- __kdebug_trace(0x9000014, t, 0, 0, 2, 0);
- #endif
- return(0);
- }
- }
- }
另外,贴下pthread_t结构:
- typedef struct _pthread
- {
- long sig; /* Unique signature for this structure */
- struct __darwin_pthread_handler_rec *__cleanup_stack;
- pthread_lock_t lock; /* Used for internal mutex on structure */
- uint32_t detached:8,
- inherit:8,
- policy:8,
- freeStackOnExit:1,
- newstyle:1,
- kernalloc:1,
- schedset:1,
- wqthread:1,
- wqkillset:1,
- pad:2;
- size_t guardsize; /* size in bytes to guard stack overflow */
- #if !defined(__LP64__)
- int pad0; /* for backwards compatibility */
- #endif
- struct sched_param param;
- uint32_t cancel_error;
- #if defined(__LP64__)
- uint32_t cancel_pad; /* pad value for alignment */
- #endif
- struct _pthread *joiner;
- #if !defined(__LP64__)
- int pad1; /* for backwards compatibility */
- #endif
- void *exit_value;
- semaphore_t death; /* pthread_join() uses this to wait for death's call */
- mach_port_t kernel_thread; /* kernel thread this thread is bound to */
- void *(*fun)(void*);/* Thread start routine */
- void *arg; /* Argment for thread start routine */
- int cancel_state; /* Whether thread can be cancelled */
- int err_no; /* thread-local errno */
- void *tsd[_EXTERNAL_POSIX_THREAD_KEYS_MAX + _INTERNAL_POSIX_THREAD_KEYS_MAX]; /* Thread specific data */
- void *stackaddr; /* Base of the stack (is aligned on vm_page_size boundary */
- size_t stacksize; /* Size of the stack (is a multiple of vm_page_size and >= PTHREAD_STACK_MIN) */
- mach_port_t reply_port; /* Cached MiG reply port */
- #if defined(__LP64__)
- int pad2; /* for natural alignment */
- #endif
- void *cthread_self; /* cthread_self() if somebody calls cthread_set_self() */
- /* protected by list lock */
- uint32_t childrun:1,
- parentcheck:1,
- childexit:1,
- pad3:29;
- #if defined(__LP64__)
- int pad4; /* for natural alignment */
- #endif
- TAILQ_ENTRY(_pthread) plist;
- void * freeaddr;
- size_t freesize;
- mach_port_t joiner_notify;
- char pthread_name[MAXTHREADNAMESIZE]; /* including nulll the name */
- int max_tsd_key;
- void * cur_workq;
- void * cur_workitem;
- uint64_t thread_id;
- } *pthread_t;
总之,线程退出不是什么多么可怕的东西,操作系统大量用线程技术,上层的软件也不应该对此如此惧怕。
pthread_exit和pthread_cancel都是不错的选择; 不过,需要注意清理函数、资源释放的正确,以减少死锁问题的产生,减少线程意外崩溃问题的产生。
作者:陈曦
日期:2012-8-5 16:13:36
环境:[Mac 10.7.1 Lion Intel i3 支持64位指令 gcc4.2.1 xcode4.2 苹果开源代码Libc-763.11]
转载请注明出处
每日总结: 优秀的架构都是类似的,垃圾的架构一般都是一个原因:代码内部原理掌握得不够
FROM: http://blog.csdn.net/cxsjabcabc/article/details/7829514