Linux线程
1. 线程的概念
Linux内核中线程实现原理
Linux线程实现原理与Windows不同。Linux下线程基于进程实现,线程和进程性能基本一致。Windows下使用线程编程比较多。
LWP:LightWeightProcess 轻量级进程 ->线程 本质仍是线程
进程有独立PCB,独立地址空间
线程有各自PCB(指向的三级页表相同 拷贝的指针指向相同的页目录 物理内存单元实际上共享),共享进程的一块地址空间
当有第一个线程创建,原进程退化为线程。
线程是最小的执行单位,进程是最小分配资源的单位,线程可以看做寄存器和栈的集合(地址 指针 局部变量)
pthread_create/fork底层函数clone,都是从父进程拷贝
内核中不区分进程和线程,以PCB为区分标志。
查看LWP线程号 (不是线程id):ps -Lf pid
LWP:CPU分配时间片的依据
线程ID:在进程内部用于区分线程
线程共享资源
同一进程内的线程共享资源:
文件描述符表、每种信号的处理方式(不建议线程和信号相关问题同时实现)、共享当前工作目录、共享用户id和组id、共享内存地址空间(.text/.data/.bss/heap/共享库 例外:errno变量)
非共享资源:
线程id
处理器现场(寄存器和栈指针)内核栈
独立的栈空间(用户空间栈)
errno
信号屏蔽字
调度优先级
优缺点
优点 :
提高并发性(多个线程相当于进程去抢占时间片)
相对于进程开销小(0~4G)
通信和共享数据容易
缺点:
pthread库函数 不稳定(相对于系统级调用)
调试和编写困难 (gdb无法使用)
对信号支持较弱
线程优点相对突出 但是Linux下进程线程区别不大
2. 线程控制原语
参数详见manpage
注意:编译链接使用-lpthread
pthread_self
返回调用线程的id(pthread_t)
pthread_create
创建线程,成功返回0,错误返回错误编号(和系统调用不同 不再设置errno),使用strerr把错误编号转换成char*
pthread_exit
退出线程(exit是退出整个进程),没有返回值
pthread_join
阻塞等待回收线程,获取退出状态。获取pthread_exit(void)的参数void*
pthread_detach
线程分离(状态上分离),不会产生僵尸线程,不需要join回收。如果回收了,join返回失败。
pthread_cancel
杀死线程,退出值为-1。注意 不是实时的 需要等待线程到达某一个取消点 (比如一个系统调用,如果线程中没有取消点,可以通过调用pthread_testcancel设置取消点)
pthread_testcancel
设置取消点
pthread_equal
比较两个线程id 是否相等
进程和线程的控制原语对比
fork pthread_createexit(int) pthread_exit(void*)wait(int*) pthread_join(,void**)//分离态不能正常回收 返回错误 进程被杀死 回收返回值-1kill pthread_cancel() //不是实时取消 取消点/检查点//取消点 系统调用 man 7 pthreadspthread_testcancel()getpid pthread_selfpthread_detach //设置分离 优点 自动清理pcb
3. 线程属性控制(扩展)
pthread_arrt_t attr;pthread_attr_init(pthread_attr_t*);pthread_attr_destroy(pthread_attr_t*);
能设置分离态(create时自动分离无需手动调用detach)
pthread_attr_setdetachstate(attr*,int state);//state : PTHREAD_CREATE_DETACH// PTHREAD_CREATE_JOINABLE//.....getdetachstate
设置线程栈的大小(进程默认8M,线程栈均分这8M空间)从堆空间申请额外空间补充
pthread_attr_setstack(attr*,void* stackaddr,size_t stacksize);//stackaddr: malloc的空间//get...
线程栈警戒缓冲区大小(两个线程栈空间之间设置警戒区)
pthread_addr_setstacksize(attr*,size_t stacksize);//getstacksize.....
4.线程注意事项
线程共享全局变量验证程序(进程不共享)
#include<unistd.h>#include<string.h>#include<stdlib.h>#include<stdio.h>int share = 0;void* fun(void* arg){share+=100;printf("tid:%lu share:%d\n",pthread_self(),share);return NULL;}int main(){pthread_t tid;for(int i=0;i<5;i++){sleep(2);int ret =pthread_create(&tid,NULL,fun,NULL);if(ret!=0){printf("err:pthread create %d\n",ret);printf("thread: %s\n",strerror(ret));fprintf(stderr,"error:%s\n",strerror(ret));exit(1);}}return 0;}
return、exit和pthread_exit的区别
exit退出进程 不能在线程中使用
main函数中使用return 0 ,会导致线程未执行完成时,主控线程结束导致全部结束。
return:返回到调用者
pthread_exit—->嵌套调用函数中也能使用,来结束线程。主线程调用 其他线程不会全部被强制结束。
回收线程
回收子进程只能由父进程,但线程之间彼此也能回收。
设置分离态后可能出现的问题
设置分离态后,假如线程运行非常快,在create函数返回前结束,它的tid可能被其他线程使用,那么调用create的线程就得到了错误的线程号。 避免:采取同步措施:pthread_cond_timedwait
malloc和mmap申请的可以被其他线程释放
应该避免在多线程中fork、exec,线程fork,子进程中只有调用fork的线程存在,其他线程在子进程中均pthread_exit
避免在多线程中使用信号机制
4. NPTL
查看NPTL库的版本:
getconf GNU_LIBPTHREAD_VERSION
编译链接:-lpthread
