linux 多线程和同步机制的使用

linux 多线程和同步机制的使用 最近正在尝试写一个脚本解析器, 其中有的地方要考虑多线程的并发,所以找了个多线程库的实现来看(linuxthreads,虽然已经被NPTL取代,但是在linuxthreads里可以看到很多原汁原味的概念,选择linuxthreads还有一个原因是我找不到pthread实现的独立包,也不想从glibc中自己分离 pthread的实现部分^_^),在看源码前,这里先看看pthread的接口说明(这里大部分的描述都是基于POSIX标准的,但是 linuxpthreads很多地方却不遵守POSIX,所以源码和这里的描述可能会有一定的差异),就作为熟悉一下POSIX的多线程模型吧 (linuxthreads的分析请参考我的另一篇blog(整理中^_^))。 1、linux内核对线程的支持实质 linux内核中没有专门针对线程的接口,在内核看来"线程"是一个"轻量级的进程",也就是说创建"线程"和创建"进程"最后调用的都是创建进程的内核接口do_fork(...)。 2、线程的取消点 取消点是针对函数来说的,表示当一个线程调用这个(多数是阻塞的)函数在调用的过程中,如果别的线程给这个线程发送了cannel信号,这个线程是否直接从这个里退出,而不从函数返回。很多不是取消点阻塞的调用会由于cancel信号而返回,从而使程序重新得到控制权继续运行,同时设置错误号为 (error = EINTR),直到遇到取消点才会退出。(为了使取消点模式有效,需要设置线程的退出方式为PTHREAD_CANCEL_DEFERED,且允许取消点 PTHREAD_CANCEL_ENABLE) 取消点的存在是为了使开发者有机会释放不再由于线程的退出而不再有用的资源或者通知其他的工作线程等。 取消点包括:pthread_testcancel、pthread_cond_wait、pthread_cond_timedwait、 pthread_join、sigwait和标志库中的一些会引起阻塞的系统调用(如:read/write等,不过有的linux平台的标准库并没有实现取消点的功能,而使通过error=EINTR的方式将阻塞的系统调用返回到控制程序中) 3、libpthread的使用 libpthread库分为thread、mutext、condvar、semaphore、rwlock几个部分,现在分别来看一下它们的API 3.1、 thread的API 创建线程 int pthread_create(pthread_t * thread, pthread_attr_t * attr, void * (*start_routine)(void *), void * arg) thread是一个指向线程结构的指针,在后续的线程函数调用中都需要它 pthread_attr_t是控制线程属性的结构: typedef struct __pthread_attr_s { int __detachstate; 设置线程是否和其他线程同步(其他线程能否调用pthread_join),也可以在新线程运行中调用 pthread_detach完成默认值是PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_DETACH),detach后线程的资源在退出后自行释放。 int __schedpolicy; 线程的调度策略,可以用pthread_setschedparam设置,有效值为SCHED_OTHER(正常、 非实时)、SCHED_RR(实时、轮转法)和SCHED_FIFO(实时、先入先出) struct __sched_param __schedparam; 调度参数,现在只有sched_priority一个成员,表示线程的优先级,只 在调度策略为SCHED_RR或SCHED_FIFO有效 int __inheritsched; 设置是否继承创建者的调度相关参数,默认是PTHREAD_EXPLICIT_SCHED,即按照attr的 参数指定 int __scope; CPU时间的竞争方式,posix支持两种:PTHREAD_SCOPE_SYSTEM、 PTHREAD_SCOPE_PROCESS(linuxthreads只实现了PTHREAD_SCOPE_SYSTEM)。 size_t __guardsize; int __stackaddr_set; void *__stackaddr; size_t __stacksize;表示堆栈的大小,通过函数 } pthread_attr_t; 设置pthread_attr_t的相关函数: 初始化:pthread_attr_init(&attr) | 初始化的值为: __scope = PTHREAD_SCOPE_PROCESS __tetachstate = PTHREAD_CREATE_JOINABL __stackaddr = NULL __stacksize = 1M __sched_param.priority = 0 -->使用创建线程的优先级 __inheritsched = PTHREAD_INHERIT_SCHED __schedpolicy = SCHED_OTHER 反初始化:pthread_attr_destroy(&attr) 设置关联标志:int pthread_attr_setdetachstate(pthread_attr_t *tattr,int detachstate); detachstate = PTHREAD_CREATE_JOINABLE/PTHREAD_CREATE_DETACHED 设置PTHREAD_CREATE_DETACHED表示线程在退出后资源自动释放,不需要调用pthread_join 查询关联标志: int pthread_attr_getdetachstate(const pthread_attr_t *tattr, int *detachstate;) 设置cpu竞争模式: int pthread_attr_setscope(pthread_attr_t *tattr,int scope); scope = PTHREAD_SCOPE_SYSTEM/PTHREAD_SCOPE_PROCESS 查询cpu竞争模式: int pthread_attr_getscope(pthread_attr_t *tattr, int scope); 设置调度策略:int pthread_attr_setschedpolicy(pthread_attr_t *tattr, int policy); policy = SCHED_OTHER/SCHED_RR/SCHED_FIFO 查询调度策略:int pthread_attr_getschedpolicy(pthread_attr_t *tattr, int policy); 设置继承模式: int pthread_attr_setinheritsched(pthread_attr_t *tattr, int inherit); inherit = PTHREAD_INHERIT_SCHED/PTHREAD_EXPLICIT_SCHED 查询继承模式:int pthread_attr_getinheritsched(pthread_attr_t *tattr, int inherit); 设置优先级:int pthread_attr_setschedparam(pthread_attr_t *tattr, const struct sched_param *param); 查询优先级:int pthread_attr_getschedparam(pthread_attr_t *tattr, const struct sched_param *param); 设置堆栈大小:int pthread_attr_setstacksize(pthread_attr_t *tattr, int size); 默认情况下线程保留1M的,而且会在堆栈的顶增加一个空闲的内存页,当访问该内存页的时候就会触发SIGSEGV信号,如果开发者设置了stack size那么就需要用户制定这个多余的内存页并且通过mprotect 函数设置保护标志,而且它必须设置PTHREAD_CREATE_JOINABLE关联模式,因为只有其他线程调用 pthread_join后分配的资源才会被释放,线程的堆栈的分配必须大于一个最小值PTHREAD_STACK_MIN()。当分配内存的时候会设置 MAP_NORESERVE标志(mmap),这个标志表示不预留交换空间,当对该内存进行写的时候,如果系统不能分配到交换空间,那么就会触发 SIGSEGV信号,如果可以分配到交换空间,那么就会把private page复制到交换空间。如果mmap没有指定MAP_NORESERVE,在分配空间的时候就会保留和映射区域相同大小的交换空间(这个其实就是资源的滞后分配原则) 查询堆栈大小:int pthread_attr_getstacksize(pthread_attr_t *tattr, int size); 设置堆栈地址:int pthread_attr_setstackaddr(pthread_attr_t *tattr,void **stackaddr); 如果线程地址为NULL,那么pthread分配指定的内存(1M)或者是指定的堆栈大小,如果设定了堆栈的地址那么内存的分配必须由开发者设定,例如: stackbase = (void *) malloc(size); --> ret = pthread_attr_setstacksize(&tattr, size); --> ret = pthread_attr_setstackaddr(&tattr, stackbase); --> ret = pthread_create(&tid, &tattr, func, arg); 查询堆栈地址:int pthread_attr_getstackaddr(pthread_attr_t *tattr,void **stackaddr); 等待线程终止 int pthread_join(thread_t tid, void **status); 等待线程结束,这个函数会阻塞调用线程,如果多个线程同时等待一个线程,只有一个线程会成功返回,其他线程将会返回错误值ESRCH(无效的线程, 等待的线程).其他错误值包括:EDEADLK:自己等待自己; EINVAL: tid无效。 将线程和其他线程脱离同步,别的线程不能对它调用pthread_join,而且它的资源也是在退出时自行释放 int pthread_detach(thread_t tid); 创建TSD key,通常在创建线程前创建,然后在新创建线程中使用 int pthread_key_create(pthread_key_t *key, void (*destructor) (void *)); void (*destructor) (void *)这是线程退出时如果TSD变量不是NULL,就会调用destructor pthread_key_create通常与pthread_once结合使用,以保证创建TSD只执行一次 static pthread_once_t key_only_one = PTHREAD_ONCE_INIT; pthread_once(&key_only_one, key_create_function); void key_create_function(void) { pthread_key_create(&key_obj, free_key); } void free_key(void *arg) { free(arg); } 删除TSD key int pthread_key_delete(pthread_key_t *key); 设定TSD key对应的值 int pthread_setspecific(pthread_key_t key, const void *value); 查询TSD key对应的值 int pthread_getspecific(pthread_key_t key); 查询线程的id pthread_t pthread_self(void); 比较线程的id int pthread_equal(pthread_t tid1, pthread_t tid2); 线程数据初始化函数,通常与pthread_create_key结合使用 int pthread_once(pthread_once_ *once_control, void (*init_routine)(void)); 放弃cpu时间 int sched_yield(void); 查询线程的调度优先级和策略 int pthread_getschedparam(pthread_t tid, int policy, struct schedparam *param); 设置线程的调度优先级和策略 int pthread_setschedparam(pthread_t tid, int policy, const struct schedparam *param); 发信号给线程 int pthread_kill(thread_t tid, int sig); 设置或查询调用线程的信号掩码 int pthread_sigmask(int how, const sigset_t *new, sigset_t *old); 不同的线程可以选择接收不同的信号,这个需要线程进行设置 在多线程环境下,注册fork回调函数 int pthread_atfork(void (*prepare) (void), void (*parent) (void), void (*child) (void) ); 这个函数注册回调函数,当线程调用fork时,系统只会复制调用的线程,其他的线程不会被复制,如果多个线程需要多个锁来同步,开发者需要保证创建线程调用fork时拥有所有的锁,否则可能造成某些线程的死锁,而且在fork和调用exec之间不要使用诸如printf这样的函数,因为它的内部锁是由 libc拥有的,这样也有可能造成死锁。同样的如果线程调用vfork来创建新的进程,由于vfork不完全复制父进程空间到子进程中(子进程仍然在父进程地址空间中运行),那么如果在调用exec前子进程修改了父进程的空间就会造成不可预测的后果。如果应用需要复制全部的线程,就需要调用forkall 函数,这时也要考虑锁的问题 pthread_atfork可以注册3个回调函数,第一个在fork前会被调用(通常获取全部的锁),第二个是fork后由父进程调用(释放锁)、第三个由子进程调用(释放锁)。 线程退出函数 void pthread_exit(void *status); 如果该线程是joinable的,那么它的资源在别的线程调用pthread_join后才会释放,如果不是joinable的那么在线程退出后就会释放。线程可以自己调用pthread_exit退出,也可以别的线程对它调用pthread_cancel退出(可能延迟到退出点),还可以从启动函数中返回退出。对于进程中的主线程有点特别,如果它调用exit来退出进程,那么所有的线程都会退出,如果只是调用pthread_exit,那么只有它自己退出,其他线程继续执行,只有所有的线程都退出后,进程才会退出。 中止一个线程,发送取消信号 int pthread_cancel(pthread_t thread); 设置线程是否允许中止(别的线程调用pthread_cannel,接收cancel信号的线程是否忽略该信号) int pthread_setcancelstate(int state, int *oldstate); state = PTHREAD_CANCEL_ENABLE/PTHREAD_CANCEL_DISABLE 设置线程的停止类型(在取消点停止/立即停止) int pthread_setcanceltype(int type, int *oldtype); type = PTHREAD_CANCEL_DEFERED/PTHREAD_CANCEL_ASYNCHRONOUS 创建取消点 void pthread_testcancel(void); 注册回调函数到线程的函数调用堆栈中,主要是释放在取消点还保留的锁 void pthread_cleanup_push(void(*routine)(void *), void *args); 这个函数一般是释放在取消点持有的锁,例如: pthread_cleanup_push(pthread_mutex_unlock,&mutex); ... pthread_mutex_lock(&mutex)); pthread_cond_wait(&cond,&mutex);--> @1 ... pthread_mutex_unlock(&mutex); ... pthread_cleanup_pop(0); 如果在@1的地方线程退出,mutex将会一直被锁定,其他等待mutex的线程就会阻塞, 通过pthread_cleanup_push就可以注册线程退出的回调代码(放到运行堆栈上),从而释放mutex 清除线程调用堆栈中的回调函数,如果execute == 1,在删除该回调函数的同时还会允许这个函数 void pthread_cleanup_pop(int execute); 3.2、互斥锁的使用 快速锁 struct _pthread_fastlock { long int __status; 表示锁是空闲/被锁定(或者是等待队列) __atomic_lock_t __spinlock; 用于compare_and_swap 竞争 }; typedef struct { int __m_reserved; 保留,将来使用 int __m_count; 锁定的递归深度 _pthread_descr __m_owner; 互斥锁拥有的线程 int __m_kind; 锁的类型:递归锁/错误检测锁/快速锁 struct _pthread_fastlock __m_lock; 快速互斥锁 } pthread_mutex_t; typedef struct { int __mutexkind; 表示互斥锁的类型 } pthread_mutexattr_t; 初始化互斥锁属性对象 int pthread_mutexattr_init(pthread_mutexattr_t *mattr); 初始化时,互斥锁的可见域的范围被设置为PTHREAD_PROCESS_PRIVATE(进程内共享),互斥锁的可见域还可以设置为PTHREAD_PROCESS_SHARED(进程间共享) (pthread的接口说明和参数设置是POSIX的标准) 释放互斥锁属性对像的资源(对于不同的实现,pthread_mutexattr_init并不一定分配资源) int pthread_mutexattr_destroy(pthread_mutexattr_t *mattr); 设置互斥锁的共享范围 int pthread_mutexattr_setpshared(pthread_mutexattr_t *mattr, int pshared); pshared = PTHREAD_PROCESS_PRIVATE/PTHREAD_PROCESS_SHARED 查询互斥锁的共享范围 int pthread_mutexattr_getpshared(pthread_mutexattr_t *mattr, int* pshared); 设置互斥锁的类型 int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) type的类型包括: PTHREAD_MUTEX_NORMAL:不检测死锁,如果等待一个已经锁定的互斥量将会一直等待,即使是同一个线程锁定互斥量两次也会造成死锁,解除有其他线程锁定的互斥量将会引起不确定行为 PTHREAD_MUTEX_ERRORCHECK:检测错误,一个线程重新锁定同一个锁会返回EDEADLK,如果解锁由其它线程锁定的互斥量或者没有锁定的互斥量就会返回错误 PTHREAD_MUTEX_RECURSIVE:线程可以多次锁定同一个互斥锁,并且需要解锁和锁定次数对应,尝试解除没有锁定的互斥锁和解除由其他线程锁定的互斥锁将会引起错误。 PTHREAD_MUTEXT_DEFAULT:重复锁定一个锁会导致不确定行为,其他和NORMAL相同.(一般来说它会映射到PTHREAD_MUTEX_NORMAL) (linuxthreads中会把PTHREAD_MUTEX_TIMED_NP/PTHREAD_MUTEX_RECURSIVE_NP /PTHREAD_MUTEX_ERRORCHECK_NP /PTHREAD_MUTEX_ADAPTIVE_NP映射到相应POSIX类型, PTHREAD_MUTEX_TIMED_NP表示在等待该锁的时候可以设置 ) 查询互斥锁的类型: int pthread_mutexattr_gettype(pthread_mutexattr_t *attr , int *type); 设置互斥锁属性的协议: int pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol); PTHREAD_PRIO_NONE:线程的优先级和调度不会受到互斥量拥有权的影响 PTHREAD_PRIO_INHERIT:当高优先级的等待一个被低优先级的线程锁定的互斥量时,低优先级 线程将以高优先级线程的优先级运行。这种方式将会以继承的形式传递。 PTHREAD_PRIO_PROTECT:拥有该类型的互斥锁的线程将以自己的优先级和它拥有的互斥锁的优先级 较高者允许,其他等待该线程拥有的锁的线程对该线程的调度优先级没有影响。 查询互斥锁的协议: int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *attr, int *protocol); 设置互斥锁的优先级上限: int pthread_mutexattr_setprioceiling(pthread_mutexatt_t *attr, int prioceiling, int *oldceiling); 为了避免优先级倒置,互斥锁的优先级上限设置为所有线程的最高优先级 获取互斥锁的优先级上限: int pthread_mutex_getprioceiling(const pthread_mutex_t *mutex, int *prioceiling); 设置互斥锁的强健属性 int pthread_mutexattr_setrobust_np(pthread_mutexattr_t *attr, int *robustness); rpnistness = PTHREAD_MUTEX_ROBUST_NP / PTHREAD_MUTEX_STALLED_NP PTHREAD_MUTEX_ROBUST_NP:表示互斥锁的属主失败,那么以后对pthread_mutex_lock调用将 将会以不确定的方式被阻塞。 PTHREAD_MUTEX_STALLED_NP:如果属主失败,那么下一个属主将会获得锁,而且错误值将设为EOWNERDEAD 如果互斥锁的属主失败,那么可能造成互斥锁的状态不一致,如果下一个属主能够保证互斥锁的一致,那么就要调用 pthread_mutex_consistent_np()并解除对该锁锁定; 如果无法保证锁一致,那么新的属主只要释放锁就可以了。所有等待的线程将会唤醒,且以后pthread_mutex_lock将会失败,返回错误 ENOTRECOVERABLE. 通过pthread_mutex_destroy/pthread_mutex_init可以恢复锁的一致性。 查询强键属性: int pthread_mutexattr_getrobust_np(const pthread_mutexattr_t *attr, int *robustness); 初始化互斥锁变量 int pthread_mutex_init(pthread_mutex_t *mp, const pthread_mutexattr_t *mattr); 静态定义的互斥量可以用宏PTHREAD_MUTEX_INITIALIZER来初始化 使互斥锁保持一致 int pthread_mutex_consistent_np(pthread_mutex_t *mutex); 锁定互斥锁 int pthread_mutex_lock(pthread_mutex_t * mutex) 解除互斥锁锁定 int pthread_mutex_unlock(pthread_mutex_t *mutex); 尝试锁定互斥锁 int pthread_mutex_trylock(pthread_mutex_t *mutex); 当互斥锁已经被锁定时,马上返回而不阻塞(EBUSY) 删除互斥锁 int pthread_mutex_destroy(pthread_mutex_t *mp); POSIX锁机制的linux实现都不是取消点,所以使用延迟类型的线程不会因为收到取消信号而离开 加锁等待,如果在线程加锁后解锁前被取消,锁将会一直被锁定。所以通常线程会在获取锁前注册 退出回调函数。 3.3、条件变量 typedef struct { struct _pthread_fastlock __c_lock; 快速锁,保护共享资源 _pthread_descr __c_waiting; 等待该信号的线程 char __padding[48 - sizeof (struct _pthread_fastlock) - sizeof (_pthread_descr) - sizeof (__pthread_cond_align_t)]; __pthread_cond_align_t __align; } pthread_cond_t; 条件变量属性 typedef struct { int __dummy; } pthread_condattr_t; 初始化条件变量属性 int pthread_condattr_init(pthread_condattr_t *cattr); 初始化时,条件变量的可见域的范围被设置为PTHREAD_PROCESS_PRIVATE(进程内共享),条件变量的可见域还可以设置为PTHREAD_PROCESS_SHARED(进程间共享) (pthread的接口说明和参数设置是POSIX的标准) 删除条件变量属性对象 int pthread_condattr_destroy(pthread_condattr_t *cattr); 设置条件变量的共享范围 int pthread_condattr_setpshared(pthread_condattr_t *cattr,int pshared); pshared =PTHREAD_PROCESS_PRIVATE/PTHREAD_PROCESS_SHARED 查询条件变量的共享范围 int pthread_condattr_getpshared(const pthread_condattr_t *cattr, int *pshared); 初始化条件变量 int pthread_cond_init(pthread_cond_t *cv, const pthread_condattr_t *cattr); 对于静态的条件变量可以通过宏PTHREAD_COND_INITIALIZER初始化。 等待条件变量 int pthread_cond_wait(pthread_cond_t *cv,pthread_mutex_t *mutex); 该函数结合互斥量来使用,在调用pthread_cond_wait前需要先锁定互斥量,而且以原子方式在设置条件变量的 状态后释放该互斥量,同时进入等待条件变量(其他线程调用pthread_cond_siganl/pthread_cond_broadcast), 以原子方式修改条件变量的状态并重新锁定互斥量。 当多个线程同时等待条件变量并且需要修改条件表达式时,系统按照阻塞线程的优先级顺序唤醒,所以在线程唤醒后需要再次检测条件表达式,以保证满足线程等待的条件。 在指定的时间内等待条件变量(绝对时间) int pthread_cond_timedwait(pthread_cond_t *cv, pthread_mutex_t *mp, const struct timespec *abstime); 这里要求使用的互斥量是TIMED类型的(在linuxthreads中定义了该类型的互斥量) 在指定的时间内等待条件变量(相对时间) int pthread_cond_reltimedwait_np(pthread_cond_t *cv, pthread_mutex_t *mp, const struct timespec *reltime); 激活条件变量(解锁一个等待线程) int pthread_cond_signal(pthread_cond_t *cv);这个函数使系统按照等待线程的优先级唤醒线程 激活条件变量(解锁所有等待线程) int pthread_cond_broadcast(pthread_cond_t *cv); 同时唤醒所有的等待线程,这些线程竞争互斥锁。 删除条件变量 int pthread_cond_destroy(pthread_cond_t *cv); pthread_cond_timedwait/pthread_cond_wait被设置为取消点,如果在调用这些函数的时候(释放互斥量前)线程收到 cancel信号,就会直接退出.这时互斥量将会继续保持锁定状态,其他等待该互斥锁的线程将会一直阻塞,所以在实际应用中,会联合 pthread_cleanup_push/pthread_cleanup_pop函数使用。 3.4、信号灯 typedef struct { struct _pthread_fastlock __sem_lock; 快速锁 int __sem_value; 信号灯的值 _pthread_descr __sem_waiting; 信号灯的等待线程 } sem_t; 初始化信号灯 int sem_init(sem_t *sem, int pshared(非0表示进程间共享), unsigned int value); 增加信号灯的计数(在支持compare_and_swap的平台上,能用于异步处理函数中) int sem_post(sem_t *sem); 减少信号灯的计数 int sem_wait(sem_t *sem); 减少信号灯的计数,如果信号灯的数为0,会马上返回 int sem_trywait(sem_t *sem); 删除信号灯 int sem_destroy(sem_t *sem); 3.5、读写锁 typedef struct { int __lockkind; 读写锁的类型 int __pshared; 读写锁的共享方式 } pthread_rwlockattr_t; 初始化读写锁属性 int pthread_rwlockattr_init(pthread_rwlockattr_t *attr); 删除读写锁属性对像 int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr); 设置读写锁对像的共享范围 int pthread_rwlockattr_setpshared(pthread_rwlockattr_t attr, int pshared); pshared = PTHREAD_PROCESS_SHARED/PTHREAD_PROCESS_PRIVATE 查询读写锁的共享范围 int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr, int *pshared); 初始化读写锁 int pthread_rwlock_init(pthrad_rwlock_t *rwlock, const pthread_rwlockattr_t *attr); 对于静态读写锁,可以用宏PTHREAD_RWLOCK_INITIALIZER初始化 以阻塞的方式获取读锁 int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock );同一个线程可以获取读锁多次,但是必须保证 调用相同次数的pthread_rwlock_unlock(),如果此线程已经拥有了写锁,那么结果将是不确定的(依赖于具体的实现) 以非阻塞的方式获取读锁 int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); 以阻塞方式获取写锁 int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock ); 一个线程不能多次获取写锁,而且不能同时拥有读锁和写锁 以非阻塞方式获取写锁 int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); 释放获取读写锁 int pthread_rwlock_unlock (pthread_rwlock_t *rwlock); 对于读锁,线程是可以多次或的的,所以释放的次数要和锁定的次数匹配 对于写锁,一个线程只能获得一次 删除读写锁 int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); 现在对于多线程的接口基本有了个大概的印象,而且api的使用方式也有了了解。 多线程的使用还有很多相关的内容(系统函数的安全级别、线程安全、异步信号安全等等),这里先不讨论^_^) 4、参考资料 这里参考了很多的资料: http://www-128.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part3/index.html http://www.chinaitpower.com/2005September/2005-09-13/199274.html http://www.llnl.gov/computing/tutorials/pthreads/ http://www.linux-foundation.org/spec/refspecs/LSB_1.3.0/gLSB/gLSB/book1.html http://users.actcom.co.il/~choo/lupg/tutorials/multi-thread/multi-thread.html http://www.mhpcc.edu/training/workshop2/pthreads/MAIN.html http://docs.sun.com/app/docs/doc/802-1949/6i5ur8qa7?l=zh_TW&a=view http://people.redhat.com/~drepper/ http://kerneltrap.org/node/422 http://docs.sun.com/app/docs/doc/819-7051/6n919hpaq?l=zh&a=view#gen-28594
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值