1、数据类型
(1)pthread_t 线程句柄
typedef unsigned long int pthread_t;
(2)pthread_attr_t 线程属性
typedef union { char __size[__SIZEOF_PTHREAD_ATTR_T]; long int __align; } pthread_attr_t;
Posix线程中的线程属性pthread_attr_t主要包括scope属性、detach属性、堆栈地址、堆栈大小、优先级。在pthread_create中,把第二个参数设置为NULL的话,将采用默认的属性配置。
pthread_attr_t的主要属性的意义如下:
- __detachstate,表示新线程是否与进程中其他线程脱离同步, 如果设置为PTHREAD_CREATE_DETACHED 则新线程不能用pthread_join()来同步,且在退出时自行释放所占用的资源。缺省为PTHREAD_CREATE_JOINABLE状态。这个属性也可以在线程创建并运行以后用pthread_detach()来设置,而一旦设置为PTHREAD_CREATE_DETACH状态(不论是创建时设置还是运行时设置)则不能再恢复到PTHREAD_CREATE_JOINABLE状态。
- __schedpolicy,表示新线程的调度策略,主要包括SCHED_OTHER(正常、非实时)、SCHED_RR(实时、轮转法)和SCHED_FIFO(实时、先入先出)三种,缺省为SCHED_OTHER,后两种调度策略仅对超级用户有效。运行时可以用过pthread_setschedparam()来改变。
- __schedparam,一个struct sched_param结构,目前仅有一个sched_priority整型变量表示线程的运行优先级。这个参数仅当调度策略为实时(即SCHED_RR或SCHED_FIFO)时才有效,并可以在运行时通过pthread_setschedparam()函数来改变,缺省为0。
- __inheritsched,有两种值可供选择:PTHREAD_EXPLICIT_SCHED和PTHREAD_INHERIT_SCHED,前者表示新线程使用显式指定调度策略和调度参数(即attr中的值),而后者表示继承调用者线程的值。缺省为PTHREAD_EXPLICIT_SCHED。
- __scope,表示线程间竞争CPU的范围,也就是说线程优先级的有效范围。POSIX的标准中定义了两个值:PTHREAD_SCOPE_SYSTEM和PTHREAD_SCOPE_PROCESS,前者表示与系统中所有线程一起竞争CPU时间,后者表示仅与同进程中的线程竞争CPU。目前LinuxThreads仅实现了PTHREAD_SCOPE_SYSTEM一值。
为了设置这些属性,POSIX定义了一系列属性设置函数,包括pthread_attr_init()、pthread_attr_destroy()和与各个属性相关的pthread_attr_getXXX/pthread_attr_setXXX函数。
2、int pthread_create(pthread_t * thread, pthread_attr_t * attr, void * (*start_routine)(void *), void * arg)
(1)说明
Linux系统下的多线程遵循POSIX线程接口,称为pthread。pthread_create是UNIX环境创建线程函数。与fork()调用创建一个进程的方法不同,pthread_create()创建的线程并不具备与主线程(即调用pthread_create()的线程)同样的执行序列,而是使其运行start_routine(arg)函数。
(2)参数和返回值
若成功则返回0,否则返回出错编号。
返回成功时,thread返回创建的线程ID。attr参数用于设置各种不同的线程属性。新创建的线程从start_routine函数的地址开始运行,该函数只有一个万能指针参数arg,如果需要向start_routine函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg的参数传入。
(3)linux线程
在LINUX中,采用的是“一对一”的线程机制。也就是一个用户线程对应一个内核线程。邦定属性就是指一个用户线程固定地分配给一个内核线程,因为CPU时间片的调度是面向内核线程(轻量级进程) 的,因此具有邦定属性的线程可以保证在需要的时候总有一个内核线程与之对应,而与之对应的非邦定属性就是指用户线程和内核线程的关系不是始终固定的,而是由系统来分配。
Linux线程在核内是以轻量级进程的形式存在的,拥有独立的进程表项,而所有的创建、同步、删除等操作都在核外pthread库中进行。pthread库使用一个管理线程(__pthread_manager(),每个进程独立且唯一)来管理线程的创建和终止,为线程分配线程ID,发送线程相关的信号(比如Cancel),而主线程(pthread_create())的调用者则通过管道将请求信息传给管理线程。
3、void pthread_exit(void* retval);
(1)说明
线程通过调用pthread_exit函数终止执行,就如同进程在结束时调用exit函数一样。这个函数的作用是,终止当前线程并返回一个指向某个对象的指针。
注意,如果没有需要返回的数据时,可以把retval设置为NULL。(一般都设置为NULL)
(2)关于参数会与pthread_join有关
pthread_exit 函数唯一的参数retval 是函数的返回代码,只要pthread_join中的第二个参数retval不是NULL,这个值将被传递给retval。
(3)关于return
在线程中调用return其实就是隐式调用pthread_exit(NULL)。因此完全可以用return代替pthread_exit(NULL)。
4、int pthread_join(pthread_t thread, void **retval);
(1)说明
pthread_join()函数,以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果线程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable的。
(2)参数和返回值
thread: 线程标识符,即线程ID,标识唯一线程。
retval: 用户定义的指针,用来存储被等待线程的返回值。
返回值 : 0代表成功。 失败,返回的则是错误号。
(3)补充说明
最后要说明的是,一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用pthread_join的线程则返回错误代码ESRCH。同一个进程的每个线程之间都可以join(即不一定是主线程和子线程,也可以是子线程和子线程),但记住上面红字的话。(红字部分都已经过验证)
5、int pthread_cancel(pthread_t thread)
(1)说明
发送终止信号给thread线程,如果成功则返回0,否则为非0值。发送成功并不意味着thread会终止。同时,该函数只是设置thread线程为Canceled状态,并不会阻塞所在线程。
(2)线程取消
一般情况下,线程在其主体函数退出的时候会自动终止,但同时也可以因为接收到另一个线程发来的终止(取消)请求而强制终止。
线程取消的方法是向目标线程发Cancel信号,但如何处理Cancel信号则由目标线程自己决定,或者忽略、或者立即终止、或者继续运行至Cancelation-point(取消点),由不同的Cancelation状态决定。
线程接收到CANCEL信号的缺省处理(即pthread_create()创建线程的缺省状态)是继续运行至取消点,也就是说设置一个CANCELED状态,线程继续运行,只有运行至Cancelation-point的时候才会退出。
在Cancelation-point终止也会触发等待该线程终止的pthread_join函数。
(3)取消点
根据POSIX标准,可以作为cancellation点的函数如下:pthread_join、pthread_cond_wait、thread_cond_timewait、pthread_testcancel、sem_wait、sigwait
下面的这些系统函数也可以成为cancellation点:accept、fcntl、open、read、write、lseek、close、send、sendmsg、sendto、connect、recv、recvfrom、recvmsg、system、tcdrain、fsync、msync、pause、wait、waitpid、nanosleep
其它函数(例如sleep)如果调用了上面的函数,那么,它们也是cancellation点。
总结一下:可以通过查看库函数来判断某个函数是否可以作为cancellation点,凡是有这句话“This function is a cancellation point”的,都可以作为cancellation点。
(4)与线程取消相关的pthread函数
int pthread_setcancelstate(int state, int *oldstate) //设置本线程对Cancel信号的反应,state有两种值:PTHREAD_CANCEL_ENABLE(缺省)和PTHREAD_CANCEL_DISABLE,分别表示收到信号后设为CANCLED状态和忽略CANCEL信号继续运行;old_state如果不为NULL则存入原来的Cancel状态以便恢复。 int pthread_setcanceltype(int type, int *oldtype) //设置本线程取消动作的执行时机,type由两种取值:PTHREAD_CANCEL_DEFFERED和PTHREAD_CANCEL_ASYNCHRONOUS,仅当Cancel状态为Enable时有效,分别表示收到信号后继续运行至下一个取消点再退出和立即执行取消动作(退出);oldtype如果不为NULL则存入运来的取消动作类型值。 void pthread_testcancel(void) //检查本线程是否处于Canceled状态,如果是,则进行取消动作,否则直接返回。
6、int pthread_attr_init(pthread_attr_t *attr);
(1)说明
pthread_attr_init是初始化一个线程对象的属性,在设置线程属性pthread_attr_t之前,通常先调用pthread_attr_init来初始化,之后来调用相应的属性设置函数。
(2)参数和返回值
attr为指向一个线程属性的指针,函数会对attr指向的 pthread_attr_t结构体进行初始化
返回0,表示函数初始化对象成功。失败时返回一个错误代码。
(3)设置属性的函数
在调用完pthread_attr_init后,就可以设置线程的属性,例如调用设置detachstate属性的函数:
int pthread_attr_setdetachstate(pthread_attr_t* attr, int detachstate);
(4)举例
pthread_attr_init (&attr); pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); ret = pthread_create (&pt_1, &attr, pthread_func_1, NULL);
7、int pthread_attr_destroy(pthread_attr_t *attr);
(1)说明
销毁一个目标结构,并且使它在重新初始化之前不能重新使用。
(2)参数和返回值
attr 为要删除的线程属性结构体指针
成功返回0,错误返回错误代码
8、int pthread_kill(pthread_t thread, int sig);
(1)说明
向指定ID的线程发送sig信号,如果线程代码内不做处理,则按照信号默认的行为影响整个进程,也就是说,如果你给一个线程发送了SIGQUIT,但线程却没有实现signal处理函数(由于signal在各版本的unix和linux中的实现方法不一致,因此现在一般用sigaction函数替代signal),则整个进程退出。记住要包含#<signal.h>头文件。
(2)参数和返回值
thread为被发送信号的进程
sig为信号
返回值:成功返回0;线程不存在返回ESRCH;信号不合法返回EINVAL。
(3)补充说明
比较常用的是通过pthread_kill(threadid,0),即传递0值,0是一个保留信号,作用是用来判断线程是不是还活着。活着则函数返回0,否则返回ESRCH。
至于sigaction用于信号处理的操作,待总结。
9、int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
(1)说明
有两种方法创建互斥锁,静态方式和动态方式。POSIX定义了一个宏PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁,方法如下:
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
在LinuxThreads实现中,pthread_mutex_t是一个结构,而PTHREAD_MUTEX_INITIALIZER则是一个结构常量。
动态方式是采用pthread_mutex_init()函数来初始化互斥锁
(2)参数和返回值
mutex如(1)所说,是一个结构(即pthread_mutex_t,一个共用体)类型的变量,表示一个锁。
参数attr指定了新建互斥锁的属性。如果参数attr为NULL,则使用默认的互斥锁属性,默认属性为快速互斥锁 。互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。
函数成功完成之后会返回零,其他任何返回值都表示出现了错误。
函数成功执行后,互斥锁被初始化为未锁住态。
(3)互斥锁的属性
pthread_mutexattr_t是一个共用体,在初始化一个锁之前,如果要对这个锁编辑属性,则需要对attr 进行编辑。具体函数如下(只列举一部分):
int pthread_mutexattr_init(pthread_mutexattr_t *mattr); //初始化 int pthread_mutexattr_destroy(pthread_mutexattr_t *mattr); //销毁属性对象的存储空间 int pthread_mutexattr_setpshared(pthread_mutexattr_t *mattr, int pshared); //设置互斥锁的范围 int pthread_mutexattr_settype(pthread_mutexattr_t *attr , int type); //设置互斥锁的类型 int pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol); //设置互斥锁的协议
10、int pthread_mutex_destroy (pthread_mutex_t *mutex)
(1)说明
因为在pthread_mutex_init()函数中会向系统申请一些资源,所以必要的时候要调用pthread_mutex_destory来销毁mutex。
(2)返回值
成功返回0,否则返回错误代码。
11、int pthread_mutex_lock(pthread_mutex_t *mutex);
(1)说明
当pthread_mutex_lock()返回时,该互斥锁已被锁定。调用该线程使该互斥锁锁住。如果该互斥锁已被另一个线程锁定和拥有,则调用该线程将阻塞,直到该互斥锁变为可用为止。
(2)返回值
在成功完成之后会返回零。其他任何返回值都表示出现了错误。
12、int pthread_mutex_trylock( pthread_mutex_t *mutex );
(1)说明
非阻塞的锁定互斥锁。函数是pthread_mutex_lock函数的非阻塞版本。如果mutex参数所指定的互斥锁已经被锁定的话,调用pthread_mutex_trylock函数不会阻塞当前线程,而是立即返回一个值来描述互斥锁的状况。
(2)返回值
在成功获得了一个mutex的锁后返回0,否则返回一个错误提示码错误。如果errno为0,但是返回值不为0,则表示mutex已被加锁。
13、int pthread_mutex_unlock(pthread_mutex_t *mutex);
(1)说明
释放互斥锁
(2)返回值
在成功完成之后会返回零。其他任何返回值都表示出现了错误。
14、pthread_t pthread_self(void)
函数作用:获得线程自身的ID。pthread_t的类型为unsigned long int,所以在打印的时候要使用%lu方式,否则将产生神奇的结果。