线程管理
linux线程包中包含了用于线程创建和线程销毁、调度、强制互斥和条件等待的函数。线程被创建时,系统内部会分配数据结构来装载线程ID、栈和程序计数器值以及其他的一些必须信息。一个进程中的各个线程共享进程的整个地址空间(代码段及全局变量等属于进程的属性,具体查看手册pthreads(7))。用ID引用线程
每个线程在进程范围内都有一个唯一的ID,用pthread_t类型表示。
(1)pthread_t pthread_self(void)
用来获取调用者自身的线程ID。
(2)pthread_t pthread_equal(pthread_t t1, pthread_t t2)
用来比较两个线程ID是否相同。因为pthread_t可能用一个结构来实现,所以才会有这个函数。
创建线程
线程一旦被创建就时可运行的,而不许要单独的启动操作。
(3)int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(start_routine)(void *), void *arg)
用来创建一个线程。thread用来保存新线程的ID, 用attr为新线程指定属性,线程有很多属性,我们可以在创建之前先填充attr,然后将其传递给pthread_create来创建一个指定属性的线程,如果传NULL,则系统用默认属性来创建线程。
线程的分离与连接
可以用pthread_detach()将线程分离,或则在创建线程时设置其attr参数直接创建一个分离线程。分离线程占用的资源会在它结束后系统自动收回。分离线程在结束后不会报告它们的状态。没有分离的线程就是可接合的,可接合线程占用的系统资源会在其它线程调用pthread_join()或进程结束时被释放,我们也可以通过pthread_join()来等待一个线程终止并获取其结束状态。
(4)int pthread_detach(pthread_t thread)
分离一个线程
(5)int pthread_join(pthread_t thread, void **retval)
等待一个线程直到其终止,retval用来保存终止线程的返回状态,如果为NULL,则无法检索返回状态了。
退出和取消
进程的终止可以通过调用exit()、执行main中的return,或者通过进程中的某个其它线程调用exit()来实现,在任何一种情况下,所有线程都会终止。在linux下,主线程终止不会导致进程终止,这点和Windows下的线程有所区别。
调用exit()会导致进程终止;调用pthread_exit()只会终止调用线程。在顶层执行return的线程相当于调用了pthread_exit(),传给该函数的参数就是该线程的返回值。当进程中最后一个线程终止后,进程也会以状态返回值0退出。
(6)void pthread_exit(void *value_ptr)
终止调用线程。
此外,线程除了可以自己调用pthread_exit()自己退出以外,还可以被同一个进程中的其它线程取消,不过这是有条件的。能否取消一个线程,取决与目标线程的类型(pthread_setcanceltype设定)和取消状态(pthread_setcancelstate设定)。
(7)int pthread_cancle(pthread_t thread)
取消一个线程,该函数只是向目标线程发送一个取消请求,然后就返回,并不会阻塞调用线程。
当一个线程收到取消请求时,如果线程处于PTHREAD_CANCEL_ENABLE状态(新线程的默认状态),则接受取消请求;如果线程处于PTHREAD_CANCEL_DISABLE状态,则取消请求会保持挂起状态,直到该线程重新使能取消状态。
(8)int pthread_setcancelstate(int state, int *oldstate)
该函数设定线程的取消状态。
如果被取消线程在取消时有一些资源需要释放,那么取消线程会引起一些问题,虽然线程可以在退出时执行一个清除函数(下面会说),但有时候这样也不能解决问题。所以,在取消状态使能的情况下,线程会接受取消请求,但何时接受是由线程的取消类型决定的。如果取消类型为PTHREAD_CANCEL_ASYNCHRONOUS,则线程可以在执行的任何地方都可以响应取消请求;如果取消类型为PTHREAD_CANCEL_DEFERRED,则只能在目标线程设定的取消点上响应取消请求,这也是默认情况。
(9)int pthread_setcanceltype(int type, int *oldtype)
设置线程的取消类型。
(10)void pthread_testcancel(void)
设定一个取消点。关于哪些函数设定了取消点,可以查看pthreads(7)。
线程还有一个清理函数栈,该栈中的函数会在以下情形被调用:
1>当线程被取消时,所有栈中的函数都会以与入栈顺序相反的顺序执行
2>当线程通过调用pthread_exit()结束时,所有栈中的函数都会以与入栈顺序相反的顺序执行。但是如果从线程的启动函数中用return语句返回,则不会执行这些函数。
3>当以非0参数调用pthread_cleanup_pop()时,会执行栈顶的函数。
(11)void pthread_cleanup_push(void (*routine)(void *), void *arg)
注册一个清理函数
(12)void pthread_cleanup_pop(int execute)
移除或执行一个清理函数。
线程安全
在多线程程序中要注意的一个问题是,它们可能会调用一些非线程安全的库函数。如果一个函数可以由多个线程同时执行而不会对结果有什么影响,那么该函数就是线程安全的。除了在pthread(7)中列出的一些函数外,标准要求其它函数都是线程安全的。
线程属性
linux下,将线程的属性封装到了pthread_attr_t类型的对象中,我们可以通过以下函数来操作这个对象,以达到设置属性的目的。
(1)int pthread_attr_destroy(pthread_attr_t *attr)
(2)int pthread_attr_init(pthread_attr_t *attr)
*_init()用线程的默认状态初始化一个属性对象;*_destory()使一个属性对象变得不可用。
线程状态
(3)int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate)
(4)int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)
设置或获取一个属性对象的当前状态是可分离的还是可接合的。默认是可接合的。
线程栈
(5)int pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr, size_t size)
(6)int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize)
设置属性对象中线程栈的起始位置和大小。
(7)int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t guardsize)
(8)int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize)
为属性对象中的线程栈设置警戒值,这个区域可能会额外分配空间,也有可能会在当前栈中分配,这取决与glibc的版本。默认的警戒值为系统一页的大小。
线程调度
对象的竞争范围控制了线程是在进程内部还是在系统级竞争调度资源。
(9)int pthread_attr_getscope(pthread_attr_t *attr, int *contentionscope)
(10)int pthread_attr_setscope(pthread_attr_t *attr, int contentionscope)
设置或获取线程对象的竞争范围。contentionscope的取值可以为PTHREAD_SCOPE_PROCESS,PTHREAD_SCOPE_SYSTEM,前者表示为进程级,后者表示为系统级。
线程会以不同的方式继承它的调度策略。
(11)int pthread_attr_getinheritesched(const pthread_attr_t *attr, int *inheritsched)
(12)int pthread_attr_setinheritesched(const pthread_attr_t *attr, int inheritsched)
设置或获取属性对象中的调度策略。inheritsched的值可取PTHREAD_INHERIT_SCHED, PTHREAD_EXPLICIT_SCHED。前者表示调度属性都从创建线程中获取;后者表示使用的是该属性对象中的调度属性。
(13)int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param);
(14)int pthread_attr_getschedparam(pthread_attr_t *attr, const struct sched_param *param);
设置或获取属性对象中的调度参数。如今调度参数实际上只有一个调度优先级。
(15)int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy)
(16)int pthread_attr_getschedpolicy(pthread_attr_t *attr, int *policy)
设置或获取属性对象中的调度策略。可取的值为SCHED_FIFO, SCHED_RR和SCHED_OTHER。
返回值及错误处理
在线程包中的大多数函数成功执行都会返回0,失败会返回错误码,在线程外检查errno是没有任何意义的,因为每个线程都会有一个独有的errno。