作者:陈曦
日期: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); }
第一个对应的_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()"); } }
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); }
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; }
执行结果:
son thread end... "exit_process is called..."
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...
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); }它开始检查了信号是否合法,并查询了线程是否合法,调用了内部函数__pthread_kill发送信号。很可惜,此函数苹果没有公开源代码。
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; }
运行结果:
"son thread will exit" grandson_thread_func
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; }
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...
在上面的那段代码中,线程的取消状态和类型没有被特定的设置,那么是采用默认的状态,那就是允许取消以及采用延迟取消的方式.延迟取消的方式就是必须线程进入取消点的时候才会进入结束。取消点在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); }
enter func cleanup_func i is 0 enter func destructkey_func [Main thread]: will end...
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...
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); }
extern int __pthread_markcancel(int);
/* * 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 */ }_pthread_setcancelstate_internal代码如下:
/* * 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); }
对于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); }它主要做了设置cancel_state标志位的事情。
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 */ }内部调用的函数_pthread_testcancel代码如下:
/* * 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_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); } } }
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]
转载请注明出处
每日总结: 优秀的架构都是类似的,垃圾的架构一般都是一个原因:代码内部原理掌握得不够