线程中用到的部分函数
int pthread_atfork( void(*prepare)(void), void(*parent)(void), void(*child)(void) ); 安全的复制
pthread_attr_t attr; 声明,初始化一个线程属性对象,属性值是缺省值,占用内存由线程库分配。
pthread_attr_destroy( pthread_attr_t *tattr ); 释放属性对象
pthread_attr_getdetachstate( const pthread_attr_t *tattr, int *detachstate );取分离状态
pthread_attr_getschedparam(&attr, ¶m);设置和获取schedparam属性
pthread_attr_init( pthread_attr_t *tattr );初始化一个线程属性对象,
pthread_attr_setdetachstate( pthread_attr_t *tattr, int detachstate ); 设置分离状态
pthread_attr_setschedparam(&attr, ¶m);设置和获取schedparam属性
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); 设置线程绑定状态的函数,它有两个参数,第一个是指向属性结构的指针,第二个是绑定类型,它有两个取值:PTHREAD_SCOPE_SYSTEM(绑定的)和PTHREAD_SCOPE_PROCESS(非绑定的)
pthread_cancel( pthread_t thread ); 退出线程
pthread_cleanup_pop(3T) 将一个善后处理函数推入退出堆栈
pthread_cleanup_push(3T) 注册一个回调函数,如果你的线程在对应的pthread_cleanup_pop之前异常退出,么系统就会执行这个回调函数
pthread_cond_t声明
pthread_cond_init() 初始化
pthread_cond_wait()使线程阻塞在一个条件变量上,
pthread_cond_timedwait()阻塞线程的函数,多了一个时间参数,时间到立刻解除阻塞。
pthread_cond_ destroy(pthread_cond_t cond)释放一个条件变量的函数。
pthread_cond_signal和函数唤醒
pthread_cond_broadcast唤醒所有等待的线程
pthread_detach( pthread_t tid );将非分离线程设置为分离线程。该函数通知线程库,当线程终止以后, tid 所指线程的内存可以被立即收回。
pthread_equal( pthread_t tid1, pthread_t tid2 ); 比较线程标识符,相同返回非零值,否则返回 0
pthread_exit(3T) 终止线程 , 终止当前线程,所有绑定在线程键上的内存将释放。如果当前线程是未分离的,该线程的标识符和推出代码( status )被保留,直到其它线程用 pthread_join() 等待当前线程的终止
pthread_getschedparam( pthread_t tid, int policy, struct schedparam *param ); 取线程的优先级
pthread_getspecific( pthread_key_t key, void**value )获取绑定在线程数据键上的值pthread_setspecific。并在指定的位置存储值
pthread_join( pthread_t tid, void **status );等待另一个线程结束的线程
pthread_key_create( pthread_key_t *key, void (*destructor)(*void) ); 为线程数据创建一个键
pthread_kill(3T) 向线程发信号 ,tid 指定的线程必须和函数当前线程在同一个进程中。 sig 为 0 时,进行错误检查,不发送信号,往往被用来检查 tid 的合法性。
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL); //按缺省的属性初始化互斥体变量mutex
pthread_mutex_lock(&mutex); // 给互斥体变量加锁
pthread_mutex_trylock(pthread_mutex_t *mutex);//pthread_mutex_lock的非阻塞模式
phtread_mutex_unlock(&mutex); // 给互斥体变量解除锁
pthread_once( pthread_once_t *once_control, void(*init_routine)(void) ); 用来调用初始化函数,只有第一次调用有效。once_control 界定了相应的初始化函数是否被调用过。
pthread_self( void );取线程标识符
sem_t sem; 声明
sem_init(&sem,0,0); 初始化
int sem_wait (sem_t* sem);解除阻塞后将sem的值减一
int sem_trywait (sem_t* sem);函数sem_wait()的非阻塞版本
int sem_post (sem_t* sem);用来增加信号量的值
int sem_getvalue (sem_t* sem);得到信号量值
int sem_destroy (sem_t* sem);删除信号量
pthread_setcancelstate( int state, int *oldstate ); 允许或禁止退出设置,缺省为允许。state为PTHREAD_CANCEL_ENABLE或PTHREAD_CANCEL_DISABLE
pthread_setcanceltype(3T)设置退出类型,可以设置成延迟类型或异步类型。缺省是延迟类型。异步类型下,线程可以在执行中的任何时候被退出。
pthread_setschedparam( pthread_t tid, int policy, const struct sched_param *param );设置线程的优先级
pthread_setspecific ( pthread_key_t key, const void *value ); 设置线程数据键,设置和某个线程数据键绑定在一起的线程数据 (一般是指针)
pthread_sigmask( int how, const sigset_t *new, sigset_t *old ); 访问当前线程的信号掩码,how 表示对当前信号掩码进行什么操作。
void pthread_testcancel( void );,创建退出点,只有当线程的退出状态是允许退出,退出类型是延迟类型时,有效。
一:多线程的属性
(1)属性结构为pthread_attr_t,
(2)属性初始化的函数为pthread_attr_init,这个函数必须在pthread_create函数之前调用。属性对象主要包括是否绑定、是否分离、堆栈地址、堆栈大小、优先级。默认的属性为非绑定、非分离、缺省1M的堆栈、与父进程同样级别的优先级。
(3)关于线程的绑定,牵涉到另外一个概念:轻进程(LWP:Light Weight Process)。轻进程可以理解为内核线程,它位于用户层和系统层之间。默认状况下,启动多少轻进程、哪些轻进程来控制哪些线程是由系统来控制的,这种状况即称为非绑定的。绑定状况下,则顾名思义,即某个线程固定的"绑"在一个轻进程之上通过设置被绑定的轻进程的优先级和调度级可以使得绑定的线程满足诸如实时反应之类的要求。
设置线程绑定状态的函数为pthread_attr_setscope,它有两个参数,第一个是指向属性结构的指针,第二个是绑定类型,它有两个取值:PTHREAD_SCOPE_SYSTEM(绑定的)和PTHREAD_SCOPE_PROCESS(非绑定的)。下面的代码即创建了一个绑定的线程。
#include <pthread.h>
pthread_attr_t attr;
pthread_t tid;
/*初始化属性值,均设为默认值*/
pthread_attr_init(&attr);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
pthread_create(&tid, &attr, (void *) my_function, NULL);
(4)现场的分离状态:
线程的分离状态决定一个线程以什么样的方式来终止自己。在上面的例子中,我们采用了线程的默认属性,即为非分离状态,这种情况下,原有的线程等待创建的线程结束。只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。而分离线程不是这样子的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。设置线程分离状态的函数为pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)。第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程)。这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在pthread_create函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用pthread_cond_timewait函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create返回。设置一段等待时间,是在多线程编程里常用的方法。但是注意不要使用诸如wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。
(5)属性中线程的优先级,它存放在结构sched_param中。用函数pthread_attr_getschedparam和函数pthread_attr_setschedparam进行存放,一般说来,我们总是先取优先级,对取得的值修改后再存放回去。下面即是一段简单的例子。(5)
#include <pthread.h>
#include <sched.h>
pthread_attr_t attr;
pthread_t tid;
sched_param param;
int newprio=20;
pthread_attr_init(&attr);
pthread_attr_getschedparam(&attr, ¶m);
param.sched_priority=newprio;
pthread_attr_setschedparam(&attr, ¶m);
pthread_create(&tid, &attr, (void *)myfunction, myarg);
二,线程的数据处理
(1)线程数据介绍:在单线程的程序里,有两种基本的数据:全局变量和局部变量。但在多线程程序里,还有第三种数据类型:线程数据(TSD: Thread-Specific Data)。它和全局变量很象,在线程内部,各个函数可以象使用全局变量一样调用它。
(2)线程数据相关的函数:主要有4个:pthread_key_create(创建一个键),pthread_setspecific(为一个键设置线程私有数据),pthread_getspecific(从一个键读取线程私有数据),pthread_key_delete(删除一个键)。这几个函数的声明如下:
#include <pthread.h>
int pthread_key_create(pthread_key_t *key,void (*destr_function)(void *));
int pthread_setspecific(pthread_key_t key,const void *pointer));
void *pthread_getspecific(pthread_key_t key);
int pthread_key_delete(pthread_key_t key);
1) 创建一个类型为 pthread_key_t 类型的变量。
2)调用 pthread_key_create() 来创建该变量。该函数有两个参数,第一个参数就是上面声明的 pthread_key_t 变量,
第二个参数是一个清理函数,用来在线程释放该线程存储的时候被调用。该函数指针可以设成 NULL ,这样系统将调用默认的清理函数。
3)当线程中需要存储特殊值的时候,可以调用 pthread_setspcific() 。该函数有两个参数,第一个为前面声明的 pthread_key_t 变量,
第二个为 void* 变量,这样你可以存储任何类型的值,
4) 如果需要取出所存储的值,调用 pthread_getspecific() 。该函数的参数为前面提到的 pthread_key_t 变量,该函数返回
void * 类型的值。
例8-4将实现如何创建和使用线程的私有数据,具体代码如下所示。
例8-4
#include <stdio.h>
#include <string.h>
#include <pthread.h>
pthread_key_t key;
void * thread2(void *arg)
{
int tsd = 5;
printf("thread %d is running\n",pthread_self());
pthread_setspecific(key,(void *)tsd);
printf("thread %d returns %d\n",pthread_self(),pthread_getspecific(key));
}
void * thread1(void *arg)
{
int tsd = 0;
pthread_t thid2;
printf("thread %d is running\n",pthread_self());
pthread_setspecific(key,(void *)tsd);
pthread_create(&thid2,NULL,thread2,NULL);
sleep(2);
printf("thread %d return %d\n",pthread_self(),pthread_getspecific(key));
}
int main(void)
{
pthread_t thid1;
printf("main thread begins running\n");
pthread_key_create(&key,NULL);
pthread_create(&thid1,NULL,thread1,NULL);
sleep(5);
pthread_key_delete(key);
printf("main thread exit\n");
return 0;
}
三,互斥锁
(1)线程互斥锁函数主要有五个
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL); //按缺省的属性初始化互斥体变量mutex
pthread_mutex_lock(&mutex); // 给互斥体变量加锁
pthread_mutex_trylock(pthread_mutex_t *mutex);//pthread_mutex_lock的非阻塞模式
phtread_mutex_unlock(&mutex); // 给互斥体变量解除锁
pthread_mutex_lock(pthread_mutex_t *mutex;
用此函数加锁时,如果 mutex 已经被锁住,当前尝试加锁 的线程就会阻塞,直到互斥锁被其他线程释放。当此函数返 回时,说明互斥锁已经被当前线程成功加锁.
pthread_mutex_trylock(pthread_mutex_t *mutex);
用此函数加锁时,如果 mutex 已经被琐主,当前尝试加锁 的线程不会阻塞,而是立即返回,返回的错误码为 EBUSY, 而不是阻塞等待。
pthread_mutex_unlock(pthread_mutex_t *mutex);
下面的代码实现了对共享全局变量x 用互斥体mutex 进行保护的目的:
#include <pthread.h>
#include <stdio.h>
#include <sys/time.h>
#include <string.h>
#define MAX 10
pthread_t thread[2];
pthread_mutex_t mut;
int number=0, i;
void *thread1()
{
printf ("thread1 : I'm thread 1\n");
for (i = 0; i < MAX; i++)
{
printf("thread1 : number = %d\n",number);
pthread_mutex_lock(&mut);
number++;
pthread_mutex_unlock(&mut);
sleep(2);
}
printf("thread1 :主函数在等我完成任务吗?\n");
pthread_exit(NULL);
}
void *thread2()
{
printf("thread2 : I'm thread 2\n");
for (i = 0; i < MAX; i++)
{
printf("thread2 : number = %d\n",number);
pthread_mutex_lock(&mut);
number++;
pthread_mutex_unlock(&mut);
sleep(3);
}
printf("thread2 :主函数在等我完成任务吗?\n");
pthread_exit(NULL);
}
void thread_create(void)
{
int temp;
memset(&thread, 0, sizeof(thread)); //comment1
//创建线程
if((temp = pthread_create(&thread[0], NULL, thread1, NULL)) != 0) //comment2
printf("线程1创建失败!\n");
else
printf("线程1被创建\n");
if((temp = pthread_create(&thread[1], NULL, thread2, NULL)) != 0) //comment3
printf("线程2创建失败");
else
printf("线程2被创建\n");
}
void thread_wait(void)
{
//等待线程结束
if(thread[0] !=0) { //comment4
pthread_join(thread[0],NULL);
printf("线程1已经结束\n");
}
if(thread[1] !=0) { //comment5
pthread_join(thread[1],NULL);
printf("线程2已经结束\n");
}
}
int main()
{
//用默认属性初始化互斥锁
pthread_mutex_init(&mut,NULL);
printf("我是主函数哦,我正在创建线程,呵呵\n");
thread_create();
printf("我是主函数哦,我正在等待线程完成任务阿,呵呵\n");
thread_wait();
return 0;
}
四,条件变量
1)条件变量描述
互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线承间的同步。
2)主要有六个函数
pthread_cond_t声明
pthread_cond_init() 初始化
pthread_cond_wait()使线程阻塞在一个条件变量上,
pthread_cond_timedwait()阻塞线程的函数,多了一个时间参数,时间到立刻解除阻塞。
pthread_cond_ destroy(pthread_cond_t cond)释放一个条件变量的函数。
pthread_cond_signal和函数唤醒
pthread_cond_broadcast唤醒所有等待的线程
3)详解函数
(1)创建和注销
条件变量和互斥锁一样,都有静态动态两种创建方式
a.静态方式
静态方式使用PTHREAD_COND_INITIALIZER常量,如下:
pthread_cond_t cond=PTHREAD_COND_INITIALIZER
b.动态方式
动态方式调用pthread_cond_init()函数,API定义如下:
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr)
尽管POSIX标准中为条件变量定义了属性,但在LinuxThreads中没有实现,因此cond_attr值通常为NULL,且被忽略。
注销一个条件变量需要调用pthread_cond_destroy(),只有在没有线程在该条件变量上等待的时候才能注销这个条件变量,否则返回EBUSY。因为Linux实现的条件变量没有分配什么资源,所以注销动作只包括检查是否有等待线程。API定义如下:
int pthread_cond_destroy(pthread_cond_t *cond)
(2)等待和激发
a.等待
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)//等待
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)//有时等待
等待条件有两种方式:无条件等待pthread_cond_wait()和计时等待pthread_cond_timedwait(),其中计时等待方式如果在给定时刻前条件没有满足,则返回ETIMEOUT,结束等待,其中abstime以与time()系统调用相同意义的绝对时间形式出现,0表示格林尼治时间1970年1月1日0时0分0秒。
无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求pthread_cond_wait()(或 pthread_cond_timedwait(),下同)的竞争条件(Race Condition)。mutex互斥锁必须是普通锁(PTHREAD_MUTEX_TIMED_NP)或者适应锁(PTHREAD_MUTEX_ADAPTIVE_NP),且在调用pthread_cond_wait()前必须由本线程加锁(pthread_mutex_lock()),而在更新条件等待队列以前,mutex保持锁定状态,并在线程挂起进入等待前解锁。在条件满足从而离开 pthread_cond_wait()之前,mutex将被重新加锁,以与进入pthread_cond_wait()前的加锁动作对应。
b.激发
激发条件有两种形式,pthread_cond_signal()激活一个等待该条件的线程,存在多个等待线程时按入队顺序激活其中一个;而pthread_cond_broadcast()则激活所有等待线程。
(3). 其他操作
pthread_cond_wait ()和pthread_cond_timedwait()都被实现为取消点,因此,在该处等待的线程将立即重新运行,在重新锁定mutex后离开 pthread_cond_wait(),然后执行取消动作。也就是说如果pthread_cond_wait()被取消,mutex是保持锁定状态的,因而需要定义退出回调函数来为其解锁。
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
/*全局变量*/
int gnum = 0;
/*互斥量 */
pthread_mutex_t mutex;
/*条件变量*/
pthread_cond_t cond;
/*声明线程运行服务程序*/
static void pthread_func_1 (void);
static void pthread_func_2 (void);
int main (void)
{
/*线程的标识符*/
pthread_t pt_1 = 0;
pthread_t pt_2 = 0;
int ret = 0;
/*互斥初始化*/
pthread_mutex_init (&mutex, NULL);
/*条件变量初始化*/
pthread_cond_init(&cond,NULL);
/*分别创建线程1、2*/
ret = pthread_create (&pt_1, //线程标识符指针
NULL, //默认属性
(void *)pthread_func_1,//运行函数
NULL); //无参数
if (ret != 0)
{
perror ("pthread_1_create");
}
ret = pthread_create (&pt_2, //线程标识符指针
NULL, //默认属性
(void *)pthread_func_2, //运行函数
NULL); //无参数
if (ret != 0)
{
perror ("pthread_2_create");
}
/*等待线程1、2的结束*/
pthread_join (pt_1, NULL);
pthread_join (pt_2, NULL);
printf ("main programme exit!/n");
return 0;
}
/*线程1的服务程序*/
static void pthread_func_1 (void)
{
int i = 0;
for (;;)
{
printf ("This is pthread1!/n");
pthread_mutex_lock(&mutex); /*获取互斥锁*/
/*注意,这里以防线程的抢占,以造成一个线程在另一个线程sleep时多次访问互斥资源,所以sleep要在得到互
斥锁后调用*/
sleep (1);
/*条件变量,当gnum<=3时,线程1自己挂起并且解锁,让线程2进去*/
while (gnum <= 3) {
pthread_cond_wait(&cond, &mutex);
}
/*当线程2调用pthread_cond_signal(&cond)时,线程1在这里重启*/
/*临界资源*/
gnum++;
printf ("Thread1 add one to num:%d/n",gnum);
pthread_mutex_unlock(&mutex); /*释放互斥锁*/
}
}
/*线程2的服务程序*/
static void pthread_func_2 (void)
{
int i = 0;
for (;;)
{
printf ("This is pthread2!/n");
pthread_mutex_lock(&mutex); /*获取互斥锁*/
/*注意,这里以防线程的抢占,以造成一个线程在另一个线程sleep时多次访问互斥资源,所以sleep要在得到互
斥锁后调用*/
sleep (1);
/*临界资源*/
gnum++;
printf ("Thread2 add one to num:%d/n",gnum);
/*当gnum == 4时,触发*/
if (gnum == 4)
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex); /*释放互斥锁*/
}
pthread_exit (0);
}
五,信号量
它们都在头文件/usr/include/semaphore.h中定义
1)主要有六个函数
sem_t sem; 声明
sem_init(&sem,0,0); 初始化
int sem_wait (sem_t* sem);解除阻塞后将sem的值减一
int sem_trywait (sem_t* sem);函数sem_wait()的非阻塞版本
int sem_post (sem_t* sem);用来增加信号量的值
int sem_getvalue (sem_t* sem);得到信号量值
int sem_destroy (sem_t* sem);删除信号量
2)
(1)sem_init
- extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));
sem为指向信号量结构的一个指针;pshared不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享;value给出了信号量的初始值。
(2)/等待信号量
函数sem_wait( sem_t *sem )被用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减一,表明公共资源经使用后减少。函数sem_trywait ( sem_t *sem )是函数sem_wait()的非阻塞版本,它直接将信号量sem的值减一。函数sem_trywait()和函数pthread_ mutex_trylock()起同样的作用,它是函数sem_wait()的非阻塞版本。
int sem_wait (sem_t* sem);
int sem_trywait (sem_t* sem);
(3)发送信号量
函数sem_post( sem_t *sem )用来增加信号量的值。当有线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不在阻塞,选择机制同样是由线程的调度策略决定的。
int sem_post (sem_t* sem);
(4)得到信号量值
int sem_getvalue (sem_t* sem);
(5)删除信号量
int sem_destroy (sem_t* sem);
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#define MAXSTACK 100
int stack[MAXSTACK][2];
int size=0;
sem_t sem;
/* 从文件1.dat读取数据,每读一次,信号量加一*/
void ReadData1(void){
FILE *fp=fopen("1.dat","r");
while(!feof(fp)){
fscanf(fp,"%d %d",&stack[size][0],&stack[size][1]);
sem_post(&sem);
++size;
}
fclose(fp);
}
/*从文件2.dat读取数据*/
void ReadData2(void){
FILE *fp=fopen("2.dat","r");
while(!feof(fp)){
fscanf(fp,"%d %d",&stack[size][0],&stack[size][1]);
sem_post(&sem);
++size;
}
fclose(fp);
}
/*阻塞等待缓冲区有数据,读取数据后,释放空间,继续等待*/
void HandleData1(void){
while(1){
sem_wait(&sem);
printf("Plus:%d+%d=%d\n",stack[size][0],stack[size][1],
stack[size][0]+stack[size][1]);
--size;
}
}
void HandleData2(void){
while(1){
sem_wait(&sem);
printf("Multiply:%d*%d=%d\n",stack[size][0],stack[size][1],
stack[size][0]*stack[size][1]);
--size;
}
}
int main(void){
pthread_t t1,t2,t3,t4;
sem_init(&sem,0,0);
pthread_create(&t1,NULL,(void *)HandleData1,NULL);
pthread_create(&t2,NULL,(void *)HandleData2,NULL);
pthread_create(&t3,NULL,(void *)ReadData1,NULL);
pthread_create(&t4,NULL,(void *)ReadData2,NULL);
/* 防止程序过早退出,让它在此无限期等待*/
pthread_join(t1,NULL);
}
六:其他
线程
创建一个缺省的线程
缺省的线程的属性:
l 非绑定
l 未分离
l 一个缺省大小的堆栈
l 具有和父线程一样的优先级
用 phread_attr_init() 创建一个缺省的属性对象,
用属性对象创建一个线程 pthread_create(3T)
int p thread_create ( pthread_t *tid, const pthread_attr_t *tattr, void *(*start_routine)(void*), void *arg );
#include<pthread.h>
pthread_attr_t tattr;
pthread_t tid;
extern void *start_routine(void *arg);
void *arg;
int ret;
/*default behavior*/
ret = pthread_create( &tid, NULL, start_routine, arg );
/*init with default attributes*/
ret = pthread_attr_init( &tattr );
/*default behavior specified*/
ret = pthread_create( &tid, &tattr, start_routine, arg );
tattr 中含有初始化线程所需的属性,值赋为 NULL 即缺省。 start_routine 是线程入口函数的起始地址。当 start_routine 返回时,相应的线程就结束了。线程结束时的退出状态值是 start_routine 函数用 phread_exit() 函数返回的返回值。当 pthread_create() 函数调用成功时,线程标识符保存在参数 tid 指针指向中。
返回值, pthread_create() 成功后返回 0 ,
EAGAIN 超过了某个限制,如 LWPs 过多。
EINVAL tattr 值非法。
创建子线程时,传给子线程的输入参数最好是 malloc() 返回的指针(这样的指针指向进程堆中的存储空间)或指向全局变量的指针,而不要是指向局部变量的指针。因为当子线程访问输入参数时,创建子线程的函数可能已结束,局部变量也就不存在了。
等待线程结束
pthread_join(3T)
int pthread_join( pthread_t tid, void **status );
#include<pthread.h>
pthread_t tid;
int ret;
int status;
/*waiting to join thread “tid” with status*/
ret = pthread_join( tid, &status );
/*waiting to join thread “tid” without status*/
ret = pthread_join( tid, NULL );
pthread_join() 会阻塞调用它的线程,直到参数 tid 指定的线程结束。 tid 指定的线程必须在当前进程中,且必须是非分离的。 status 接收指定线程终止时的返回状态码。
不能有多个线程等待同一个线程终止,否则返回错误码 ESRCH
当 pthread_join() 返回时,终止线程占用的堆栈等资源已被回收。
返回值:成功返回 0
ESRCH tid 指定的线程不是一个当前线程中合法且未分离的线程。
EDEADLK tid 指定的是当前线程。
EINVAL tid 非法。
分离一个线程
pthread_detach(3T)
将非分离线程设置为分离线程。
int pthread_detach( pthread_t tid );
#include<pthread.h>
pthread_t tid;
int ret;
ret = pthread_detach( tid );
该函数通知线程库,当线程终止以后, tid 所指线程的内存可以被立即收回。
返回值:成功返回 0
EINVAL tid 所指线程不是一个合法线程。
ESRCH tid 指定的线程不是一个当前线程中合法且未分离的线程。
为线程数据创建一个键
多线程的 c 语言程序具有三种数据:局部变量,全局变量,线程数据( TSD )
TSD 类似于全局变量,但是线程私有的 。
每个 TSD 都有个键同他相关联。
pthread_key_create(3T)
int pthread_key_create( pthread_key_t *key, void (*destructor)(*void) );
#include<pthread.h>
pthread_key_t key;
int ret;
/*key create without destructor*/
ret = pthread_key_create( &key, NULL );
/*key create with destructor*/
ret = pthread_key_destructor( &key, destructor );
该函数成功时,份配的建放在 key 中,必须保证 key 指向的内存区有效 。 destructor 用来释放不再需要的内存。
返回值:成功返回 0
EAGAIN key 名字空间耗尽
ENOMEM 没有足够的内存空间创建一个新的键。
删除线程数据的键
pthread_key_delete(3T)
solaris 线程接口中没有该函数
int pthread_key_delete ( pthread_key_t key );
#include<pthread.h>
pthread_key_t key;
int ret;
ret = pthread_key_delete( key );
在调用该函数之前,必须释放和本线程相关联的资源, pthread_key_delete() 不会引发键的解析函数。
返回值:成功返回 0
EINVAL key 值非法
设置线程数据键
pthread_setspecific(3T)
设置和某个线程数据键绑定在一起的线程数据 (一般是指针)
函数将指向专有数据的指针value 设置进 由key指示的结构体 中;
int pthread_setspecific ( pthread_key_t key, const void *value );
#include<pthread.h>
pthread_key_t key;
void *value;
int ret;
ret = pthread_setspecific( key, value );
返回值:成功返回 0
ENOMEM 没有足够的虚拟内存
EINVAL key 值非法
pthread_setspecific() 不释放原来绑定在键上的内存,给一个键绑定新的指针时,必须释放原指针指向的内存 ,否则会发生内存泄漏。
获取线程数据键
pthread_getspecific(3T)
获取绑定在线程数据键上的值,并在指定的位置存储值
返回存放在对应结构体中的专有指针;
int pthread_getspecific( pthread_key_t key, void**value )
#include<pthread.h>
pthread_key_t key;
void *value;
pthread_getspecific( key, &value );
返回值:不返回错误码
取线程标识符
pthread_self(3T)
取当前线程的标识符
pthread_t pthread_self( void );
#include<pthread.h>
pthread_t tid;
tid = pthread_self();
返回值:当前线程标识符。
比较线程标识符
pthread_equal(3T)
int pthread_equal( pthread_t tid1, pthread_t tid2 );
#include<pthread.h>
pthread_t tid1,tid2
int ret;
ret = pthread_equal( tid1, tid2 );
返回值:如果 tid1 和 tid2 相同返回非零值,否则返回 0 。如果参数非法,返回值不可预知。
初始化线程
pthread_once(3T)
用来调用初始化函数,只有第一次调用有效。
int pthread_once( pthread_once_t *once_control, void(*init_routine)(void) );
#include<pthread.h>
pthread_once_t once_control = PTHREAD_ONCE_INIT;
int ret;
ret = pthread_once( &once_control, init_routine );
once_control 界定了相应的初始化函数是否被调用过。
返回值:成功返回 0
EINVAL 某个参数为 NULL
出让当前线程对处理器的控制权
sched_yeild(3R)
把当前线程的优先权让给有相同或更高优先级的线程。
int sched_yeild( void );
#include<pthread.h>
int ret;
ret = sched_yeild();
返回值:成功返回 0
ENOSYS 当前版本不支持 sched_yield()
设置线程的优先级
pthread_setschedparam(3T)
int pthread_setschedparam( pthread_t tid, int policy, const struct sched_param *param );
#include<pthread.h>
pthread_t tid;
int ret;
struct sched_param param;
int priority;
/*sched_priority will be the priority of the thread*/
sched_param,sched_priority = priority;
/*only supported policy ,other will result in ENOTSUP*/
policy = SCHED_OTHER;
/*scheduling parameters of target thread*/
ret = pthread_setschedparam( tid, policy, ¶m );
返回值:成功返回 0
EINVAL 属性值非法
ENOTSUP 属性值在当前版本不支持
取线程的优先级
pthread_getschedparam(3T)
int pthread_getschedparam( pthread_t tid, int policy, struct schedparam *param );
#include<pthread.h>
pthread_t tid;
sched_param param;
int prioprity;
int policy;
int ret;
/*scheduling parameters of target thread*/
ret = pthread_getschedparam( tid, &policy, ¶m );
/*sched_priority contains the priority of the thread*/
priority = param.sched_priority;
返回值:成功返回 0
ESRCH tid 不是一个现存的线程。
向线程发信号
pthread_kill(3T)
int pthread_kill( pthread_t tid, int sig );
#include<pthread.h>
#include<signal.h>
int sig;
pthread_t tid;
int ret;
ret = pthread_kill( tid, sig );
tid 指定的线程必须和函数当前线程在同一个进程中。
sig 为 0 时,进行错误检查,不发送信号,往往被用来检查 tid 的合法性。
返回值:成功返回 0
EINVAL sig 不是合法信号量
ESRCH tid 不是当前进程中的线程
访问当前线程的信号掩码
pthread_sigmask(3T)
int pthread_sigmask( int how, const sigset_t *new, sigset_t *old );
#include<pthread.h>
#include<signal.h>
int ret;
sigset_t old, new;
ret = pthread_sigmask( SIG_SETMASK, &new, &old );
ret = pthread_sigmask( SIG_BLOCK, &new, &old );
ret = pthread_sigmask( SIG_UNBLOCK, &new, &old );
how 表示对当前信号掩码进行什么操作。
SIG_SETMASK :在信号掩码中加入 new 信号集, new 表示新增加的要屏蔽的信号。
SIG_BLOCK :在信号掩码中删去 new 信号集, new 表示新增加的不需再屏蔽的信号。
SIG_UNBLOCK :用 new 信号集替换信号掩码, new 表示所有需要屏蔽的信号。
当 new 为 NULL 时,无论 how 是什么,当前线程的信号掩码都不会改变。
旧的信号掩码保存在 old 中。
返回值:成功返回 0
EINVAL how 的值未定义
安全的复制
pthread_atfork(3T)
int pthread_atfork( void(*prepare)(void), void(*parent)(void), void(*child)(void) );
终止线程
pthread_exit(3T)
void pthread_exit(void *status);
#include<pthread.h>
int status;
pthread_exit( &status );
终止当前线程,所有绑定在线程键上的内存将释放。如果当前线程是未分离的,该线程的标识符和推出代码( status )被保留,直到其它线程用 pthread_join() 等待当前线程的终止。如果当前线程是分离的, status 被忽略,线程标识符立即收回。
返回值:若 status 不为 NULL ,线程的退出代码被置为 status 指向的值。
一个线程可以用一下方式终止自身运行。
从线程的入口函数返回。
调用 pthread_exit()
用 POSIX 的 pthread_cancel()
退出可以用以下方式:
异步的
由线程库定义的一系列退出点
有应用程序定义的一系列退出点
退出点
由程序通过 pthread_testcancel() 建立
调用了 pthread_cond_wait() 或 pthread_cond_timedwait() 等待一个条件的线程
调用了 pthread_join() 等待另一个线程结束的线程。
被阻塞在 sigwait(2) 上的线程。
一些标准的库函数。
退出线程
pthread_cancel(3T)
int pthread_cancel( pthread_t thread );
#include<pthread.h>
pthread_t thread;
int ret;
ret = pthread_cancel ( thread ) ;
返回值:成功返回 0
ESRCH 无指定的线程。
允许或禁止退出
pthread_setcancelstate(3T)
缺省是允许退出的。
int pthread_setcancelstate( int state, int *oldstate );
#include<pthread.h>
int oldstate;
int ret;
/*enable*/
ret = pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldstate );
/*disabled*/
ret = pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate );
返回值:成功返回 0
EINVAL state 值非法
设置退出类型
pthread_setcanceltype(3T)
可以设置成延迟类型或异步类型。缺省是延迟类型。异步类型下,线程可以在执行中的任何时候被退出。
int pthread_setcanceltype( int type, int *oldtype );
#include<pthread.h>
int oldtype;
int ret;
/*deferred mode*/
ret = pthread_setcanceltype( PTHREAD_CANCEL_DEFERRED, &oldtype );
/*async mode*/
ret = pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype );
返回值:成功返回 0
EINVAL state 值非法
创建退出点
pthread_testcancel(3T)
void pthread_testcancel( void );
#include<pthread.h>
pthread_testcancel();
只有当线程的退出状态是允许退出,退出类型是延迟类型时,有效。
没有返回值。
将一个善后处理函数推入退出堆栈
pthread_cleanup_push(3T)
pthread_cleanup_pop(3T)
void Pthread_cleanup_push( void(*routine)(void*), void *args );
void pthread_cleanup_pop( int execute );
#include<pthread.h>
/*push the handler “routine” on cleanup stack*/
pthread_cleanup_push( routine, arg );
/*pop the “func” out of cleanup stack and execute “func”*/
pthread_cleanup_pop( 1 );
/*pop the “func” and don’t execute “func”*/
pthread_cleanup_pop( 0 );
1. 线程属性
只能在线程创建时制定属性,不能在运行时改变它。
一旦属性对象被配置好,它在进程范围内都是有效的。
初始化属性
pthread_attr_init(3T)
初始化一个线程属性对象,属性值是缺省值,占用内存由线程库分配。
int pthread_attr_init( pthread_attr_t *tattr );
#include<pthread.h>
pthread_attr_t tattr;
int ret;
/*initialize an attribute to the default value*/
ret = pthread_attr_init( &tattr );
属性对象的缺省值:
scope (线程域) PTHREAD_SCOPE_PROCESS
Detachstate (分离状态) PTHREAD_CREATE_JOINABLE
Stackaddr (堆栈地址) NULL
Stacksize (堆栈大小) 1Mb
priority (优先级) 父进程优先级
Inheritsched (继承调度优先级) PTHREAD_INHERIT_SCHED
schedpolicy (调度策略) SCHED_OTHER
返回值:成功返回 0
ENOMEM 没有足够的内存初始化线程属性对象
释放属性对象
pthread_attr_destroy(3T)
int pthread_attr_destroy( pthread_attr_t *tattr );
#include<pthread.h>
pthread_attr_t tattr;
int ret;
ret = pthread_attr_destroy( &tattr );
返回值:成功返回 0
EINVAL tattr 值非法
设置分离状态
pthread_attr.setdetachstate(3T)
设置线程绑定状态的函数为pthread_attr_setscope,它有两个参数,第一个是指向属性结构的指针,第二个是绑定类型,它有两个取值:PTHREAD_SCOPE_SYSTEM(绑定的)和PTHREAD_SCOPE_PROCESS(非绑定的)
创建线程时,如果指定这个线程为分离线程,一旦这个线程终止,他的线程标识符和其他相关的资源可以立即被使用。如果不需要等待某个线程终止,可以把它设定为分离。
int pthread_attr_setdetachstate( pthread_attr_t *tattr, int detachstate );
#include<pthread.h>
pthread_attr_t tattr;
int ret;
/*set the thread detach state*/
ret = pthread_attr_setdetachstate( &tattr, PTHREAD_CREATE_DETACHED );
返回值:成功返回 0
EINVAL tattr 的值或 detachstate 的值非法
取分离状态
pthread_attr_getdetachstate(3T)
int pthread_attr_getdetachstate( const pthread_attr_t *tattr, int *detachstate );
#include<pthread.h>
pthread_attr_t tattr;
int detachstate;
int ret;
ret = pthread_attr_getdetachstate( &tattr, &detachstate );
返回值:成功返回 0
EINVAL tattr 的值或 detachstate 的值非法