1.编程头文件<pthread.h>,在gcc编译时还需要主动附加库,比如 “gcc a.c -lpthread -o a” 这样
2.线程创建函数
int pthread_create (pthread_t *tid, pthread_attr_t *attr, void *(*start_rtn)(void *), void* arg);
参数说明:
pthread_t *tid //线程ID,与进程PID是不同的概念
pthread_attr_t *attr //线程属性,NULL:线程调度方式和优先级都默认继承父线程,也可以进行配置将线程设置为RT、RR等不同调度方式的线程,并设置指定优先级
void *(*start_rtn)(void *) //所创建线程的实体函数,参数需要指定为void*
void* arg //默认NULL,需要传参就给参数
return //创建成功返回0,创建失败返回非0
3.线程的结合与分离
概念:
线程分为可结合(joinable)和分离的(detached)两种。
可结合的线程能够被其他线程回收其资源和杀死,在被其他线程回收之前,它的存储器资源(如栈)是不可释放的。
分离的线程是不能
int pthread_join(pthread_t thread, void **retval);
参数说明:
pthread_t thread; //线程ID
void **retval; //指向一个指向被连接线程的返回码的指针的指针,NULL就可以了
return; //线程结束返回0,失败返回非0
够被其他线程杀死回收或杀死,它的存储器资源在它终止时由系统自动释放。
实现方式:
线程的默认属性是可结合的,即需要其他线程回收其资源。使用pthread_join()函数等待该线程终止,并释放资源后函数才返回,其过程是阻塞的。
int pthread_join(pthread_t thread, void **retval);
参数说明:
pthread_t thread; //线程ID
void **retval; //指向一个指向被连接线程的返回码的指针的指针,NULL就可以了
return; //线程结束返回0,失败返回非0
分离线程可在线程被创建后,在线程函数内设置。使用pthread_detach(pthread_self())来更改自身属性为分离的。分离线程是不可被其他线程终止的。
4.可结合线程的终止pthread_cancel()
分离线程只能自身结束,其他线程是无法结束分离线程的运行的。可结合线程是可以被其他线程所终止的,其他线程可以通过pthread_cancel()函数来向指定线程发送结束的消息,被指定的线程会取消运行,然后返回。发送线程还需要用pthread_join来回收资源。
取消点:在一个时间段内,程序被挂起时,可以被取消的一个时间点。当线程出现block(阻塞)时,这个被阻塞的地方就是可以被取消的地方。举例:线程A执行过程中,如果遇到线程B执行pthread_cancel (),线程A会继续运行,直到某一行代码出现阻塞(如:pthread_testcancel、pthread_join、pthread_cond_wait、printf、sleep、read、write等都是可以产生阻塞的函数),此时就会退出。
int pthread_cancel(pthread_t thread);
参数说明:
pthread_t thread; //需要终止的线程ID
return; //成功返回0,错误返回errno。会阻塞
线程可以设置对cancel信号的反应,线程终止状态cancelstate和线程终止类型canceltype。
int pthread_setcancelstate(int state,int *oldstate)
参数说明:
int state; //PTHREAD_CANCEL_ENABLE:线程对cancel信号立即有反应,将设置为CANCEL状态(默认)
//PTHREAD_CANCEL_DISABLE:线程state设为不可取消,线程不理会信号,继续执行,而使用cancel函数的线程会一直阻塞到可取消状态。
int *oldstate; //NULL
//不为NULL:获取当前属性
return; //成功返回0,错误返回errno。EINVAL:无效state
int pthread_setcanceltype(int type,int *oldtype)
参数说明:
int type; //PTHREAD_CANCEL_DEFERRED:运行到下一个取消点就退出(默认)
//PTHREAD_CANCEL_ASYNCHRONOUS:直接退出
int *oldtype; //NULL
//不为NULL:获取原来的属性
return; //成功返回0,错误返回errno。EINVAL:无效的type
5.可结合线程异常退出的资源释放问题
线程终止有两种情况:正常终止和非正常终止。线程主动调用pthread_exit()或者return退出都将使线程正常退出,这是可预见的退出方式。非正常终止是线程在其他线程干预下,或者由于自身运行出错(比如访问非法地址)而退出,这种退出方式是不可预见的。
不可预见的终止会存在资源释放问题,比如堆区内存的申请但没有释放,上锁但是没有解锁就异常退出,这是必须要解决的问题。目前可采pthread_cleanup_push()/pthread_cleanup_pop()函数用于自动释放资源。从pthread_cleanup_push()的调用点到pthread_cleanup_pop()之间的程序段中的终止动作(包括调用pthread_exit(0)和取消点终止)都将执行pthread_cleanup_push()所指定的清理函数。
void pthread_cleanup_push(void (*routine)(void*), void *arg)
参数说明:
void (*rountine)(void*); //清理函数
void *arg; //传递的回调函数的参数
void pthread_cleanup_pop(int execute)
参数说明:
int execute; //0:非异常退出执行到该函数时,只是弹出清理函数,但并不执行
//1:非异常退出执行到该函数时,弹出清理函数,并执行
pthread_cleanup_push()/pthread_cleanup_pop()采用先入后出的栈结构管理,清理函数在被调用pthread_cleanup_push()函数时被压入函数栈,在pthread_cleanup_pop()时被弹出函数栈,所以是一个先进后出的策略。pthread_cleanup_push()/pthread_cleanup_pop()可以理解成‘{’和‘}’,在代码结构中要成对出现。
pthread_cleanup_pop()中execute代表在程序正常调用时,在弹出清理函数决定是否调用的情况。如果execute为0,在程序正常调用到pthread_cleanup_pop(),则只弹出清理函数不执行;反之则弹出清理函数并执行。但线程在通过pthread_exit()和取消点终止时,不论execute为0为1,都会弹出清理函数并执行。而且pthread_cleanup_push()和pthread_cleanup_pop()之前不能存在return退出,这样就不会调用pthread_cleanup_pop(),函数栈就会囤积清理函数,内核会报错。
6.线程调度
不想写了,用到再总结