线程概念:
LWP: light weight process 轻量级进程,本质仍是进程(在 linux环境下)
进程:有独立的 进程地址空间。 有独立的pcb。 分配资源最小单位
线程:有独立的 pcb。 没有独立的进程地址空间 执行最小单位
区别 :在于是否共享地址空间。 独居(进程) 合租(线程)
ps-Lf 进程id --》线程号 LWP -->CPU 执行的最小单位
在cpu眼里 线程 还是进程 一个进程中线程越多 获得cpu概率就越大 多到一定数量时概率又会下降
linux 内核线程实现原理:
类Unix 系统中,早期是没有线程概念的,80 年代才引入,借助进程机制实现了线程的概念。
因此在这类系统中,进程和线程关系密切。
1.轻量级进程(lwp) ,也有pcb,创建线程使用的底层函数和进程一样 都是 clone。
2.从内核看进程和线程是一样的,都有各自不同的 pcb,但是pcb中指向内存资源的 三级页表是相同的
3.进程可以蜕变成线程
4.线程可以看做寄存器和栈的集合。
5.在linux下,线程是最小的执行单位,进程是最小的分配资源单位
线程共享资源:
1.文件描述符
2.每种信号的处理方式
3.当前工作目录
4.用户 ID 和组 ID
5.内存地址空间 (.text/.data/.bss/heap/共享库)
非共享资源:
1.线程ID
2.处理器现场(寄存器的值)和栈指针(内核栈 用来保存寄存器的值)
3.独立的栈空间(用户空间栈)
4。errno 变量
5. 信号屏蔽字
6.调度优先级
线程优缺点:
优点: 1.提升程序并发性 2.开销小 3.数据通信 。共享数据方便
缺点: 1.库函数不稳定 2 调试 、编写困难 、gdb不支持 3.对信号支持不好
优点相对突出 、 确定均不是硬伤。 linux 下由于 实现方法导致进程线程 差别不是很大
//线程共享全局变量
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
int var = 100;
void sys_err(const char* str){
perror(str);
exit(1);
}
void* tfn(void* arg){
(void)arg;
var = 200;
printf("thread ,var=%d\n",var);
return NULL;
}
int main(){
printf("At first var =%d\n",var);
pthread_t tid;
pthread_create(&tid,NULL,tfn,NULL);
sleep(1);
printf("after pthread_create,var=%d\n",var);
return 0;
}
输出:
At first var =100
thread ,var=200
after pthread_create,var=200
线程控制原语:
检查出错返回: 线程中
fprintf(stderr,"xxxerror %s\n",strerror(ret))
pthread_t pthread_self(void); 获取线程ID 是在进程地址空间内部,用来标识线程身份的id号
返回值:本线程id
线程ID:在进程中 用来标识线程身份
线程号 lwp:标识线程身份 给cpu看的
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
参数1:传出参数,表新创建的子线程id
参数2:线程属性。传NULL表示使用默认属性
参数3:子线程回调函数。创建成功,pthread_create 函数返回时,该函数会被自动调用
参数4:参数3 的参数 没有的话 传NULL
返回值:成功 :0
失败: errno
//创建一个线程
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
void sys_err(const char* str){
perror(str);
exit(1);
}
void* tfn(void* arg){
(void)arg;
printf("thread: pid =%d,tid= %lu\n",getpid(),pthread_self());
return NULL;
}
int main(){
pthread_t tid;
tid = pthread_self();
printf("%lu\n",tid);
int ret = pthread_create(&tid,NULL,tfn,NULL);
if(ret!=0){
perror("pthread_create error");
}
printf("main: pid =%d,tid=%lu\n",getpid(),pthread_self());
//sleep(1);
// return 0;
pthread_exit((void*)0);
}
输出:
139693159536448
main: pid =11447,tid=139693159536448
thread: pid =11447,tid= 139693151209216
循环创建 N个子线程:
for(i=0;i<5;i++){
pthread_create(&tid,NULL,tfd,(void*)i); //将int 类型 i ,强转成 void* ,传参
void pthread_exit(void* retval); 退出当前线程
retval: 退出值。 无退出值时 传 NULL
exit() : 退出当前进程
return :返回到调用者那里去
pthread_exit(): 退出当前线程
//循环创建 5 个线程
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
void sys_err(const char* str){
perror(str);
exit(1);
}
void* tfn(void* arg){
(void)arg;
int i = (int)arg;//强转
sleep(1);
printf(":i'm %dth thread: pid =%d,tid= %lu\n",i+1,getpid(),pthread_self());
return NULL;
}
int main(){
int i;
int ret;
pthread_t tid;
for(i=0;i<5;++i){
ret = pthread_create(&tid,NULL,tfn,(void*)i); //传参采用 值传递 借助强转
if(ret!=0){
sys_err("pthread_create error");
}
}
printf("i'm main ,pid =%d,tid=%lu\n",getpid(),pthread_self());
sleep(i);
return 0;
}
输出:i'm main ,pid =11998,tid=140291477743424
:i'm 3th thread: pid =11998,tid= 140291452630784
:i'm 4th thread: pid =11998,tid= 140291444238080
:i'm 1th thread: pid =11998,tid= 140291469416192
:i'm 2th thread: pid =11998,tid= 140291461023488
:i'm 5th thread: pid =11998,tid= 140291435845376
int pthread_join(pthread_t thread,void** retval); 阻塞 回收线程
thread: 待回收线程 ID
retval: 传出参数, 回收的那个线程的退出值
线程异常退出 值为 -1;
返回值:成功: 0
失败:errno
// pthread_join 函数 列子
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
void sys_err(const char* str){
perror(str);
exit(1);
}
void* tfn(void* arg){
(void)arg;
return (void*)66;
}
int main(){
pthread_t tid[5];
int* retval;
int i;
for(i=0;i<5;++i){
int ret = pthread_create(&tid[i],NULL,tfn,NULL);
if(ret!=0){
sys_err("pthread_create error");
}
ret = pthread_join(tid[i],(void**)&retval);
if(ret!=0){
sys_err("pthread_join error");
}
printf("child thread exit with %d\n",retval);
}
pthread_exit(NULL);
}
输出:
child thread exit with 66
child thread exit with 66
child thread exit with 66
child thread exit with 66
child thread exit with 66
// join 函数 列子 二
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
struct thrd{
int var;
char str[256];
};
void sys_err(const char* str){
perror(str);
exit(1);
}
void* tfn(void* arg){
(void)arg;
struct thrd* tval;
tval=(struct thrd*)malloc(sizeof(tval));
tval->var=100;
strcpy(tval->str,"hello thread");
return (void*)tval;
}
int main(){
pthread_t tid;
struct thrd* retval;
int ret = pthread_create(&tid,NULL,tfn,NULL);
if(ret!=0){
sys_err("pthread_create error");
}
ret = pthread_join(tid,(void**)&retval);
if(ret!=0){
sys_err("pthread_join error");
}
printf("child thread exit with var = %d,str =%s\n",retval->var,retval->str);
pthread_exit(NULL);
}
输出:
child thread exit with var = 100,str =hello thread
int pthread_detach(pthread_t thread); 设置线程分离
设置线程分离,线程终止会自动清理pcb 无需回收
thread:待分离的线程id
返回值:成功: 0
失败:errno
//设置线程 分离
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
void sys_err(const char* str){
perror(str);
exit(1);
}
void* tfn(void* arg){
(void)arg;
printf("thread: pid =%d,tid= %lu\n",getpid(),pthread_self());
return NULL;
}
int main(){
pthread_t tid;
int ret = pthread_create(&tid,NULL,tfn,NULL);
if(ret!=0){
fprintf(stderr,"pthread_create error:%s\n",strerror(ret));
exit(1);
}
ret = pthread_detach(tid);//设置线程分离,线程终止会自动清理pcb 无需回收
if(ret!=0){
fprintf(stderr,"pthread_detach error:%s\n",strerror(ret));
exit(1);
}
sleep(1);
ret = pthread_join(tid,NULL);
printf("join ret =%d\n",ret);
if(ret!=0){
fprintf(stderr,"pthread_join error:%s\n",strerror(ret));
exit(1);
}
printf("main : pid=%d.tid=%lu\n",getpid(),pthread_self());
pthread_exit((void*)0);
}
输出:thread: pid =13036,tid= 140193968563968
join ret =22
pthread_join error:Invalid argument
int pthread_cancel(pthread_t thread); 杀死一个线程 。 需要到取消点(保存点)
thread : 待杀死线程id
返回值: 成功: 0
失败: errno
如果,子线程没有到达取消点, 那么 pthread_cancel 无效
我们可以在程序中,手动添加一个取消点 。使用pthread_testcancel()
成功被 pthread_cancel()杀死的线程 返回 -1 使用pthread_join 回收
//pthread_cancel
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
void* tfn(void* arg){
(void)arg;
while(1){
printf("thread :pid =%d,tid=%lu\n",getpid(),pthread_self());
sleep(1);
}
return NULL;
}
int main(){
pthread_t tid;
int ret = pthread_create(&tid,NULL,tfn,NULL);
if(ret!=0){
fprintf(stderr,"pthread_create errror :%s\n",strerror(ret));
exit(1);
}
printf("main: pid =%d,tid=%lu\n",getpid(),pthread_self());
sleep(3);
ret = pthread_cancel(tid);//终止线程
if(ret!=0){
fprintf(stderr,"pthread_create errror :%s\n",strerror(ret));
exit(1);
}
while(1);
pthread_exit((void*)0);
}
输出:
main: pid =13230,tid=140582995310400
thread :pid =13230,tid=140582986983168
thread :pid =13230,tid=140582986983168
thread :pid =13230,tid=140582986983168
^C
// 杀死线程 3种 方法
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
void* tfn1(void* arg){
(void)arg;
printf("thread 1 returning \n");
return (void*)111;
}
void* tfn2(void* arg){
(void)arg;
printf("thread 2 exiting\n");
pthread_exit((void*)222);
}
void* tfn3(void* arg){
(void)arg;
while(1){
// printf("thread 3 :i'm going to die in 3 seconds---\n");
// sleep(1);
//printf sleep 都有系统调用 都会进内核
pthread_testcancel();//自己添加取消点
}
return (void*)666;
}
int main(){
pthread_t tid;
void* tret = NULL;
pthread_create(&tid,NULL,tfn1,NULL);
pthread_join(tid,&tret);
printf("thread 1 eixt code = %d\n",tret);
pthread_create(&tid,NULL,tfn2,NULL);
pthread_join(tid,&tret);
printf("thread 2 eixt code = %d\n",tret);
pthread_create(&tid,NULL,tfn3,NULL);
sleep(3);
pthread_cancel(tid);
pthread_join(tid,&tret);
printf("thread 3 eixt code = %d\n",tret);
pthread_exit((void*)0);
}
输出:
thread 1 returning
thread 1 eixt code = 111
thread 2 exiting
thread 2 eixt code = 222
thread 3 eixt code = -1
线程控制原语 进程控制原语
pthread_create() fork()
pthread_self() getpid()
pthread_eixt() exit()
pthread_join() wait()/waitpid()
pthread_cancel() kill()
pthread_detach()
线程属性:
设置分离属性
pthread_attr_t attr 创建一个线程属性结构体变量
pthread_attr_init(&attr) 初始化线程属性
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED) 设置线程属性为 分离太
pthread_create(&tid,&attr,tfn,NULL) 借助修改后的 设置线程属性 创建为分离态的新线程
pthread_attr_destroy(&attr) 销毁线程属性
//创建线程之前 设置分离属性
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
void* tfn(void* arg){
(void)arg;
printf("thread: pid =%d,tid= %lu\n",getpid(),pthread_self());
return NULL;
}
int main(){
pthread_t tid;
pthread_attr_t attr;
int ret = pthread_attr_init(&attr);
if(ret!=0){
fprintf(stderr,"attr_init error%s\n",strerror(ret));
exit(1);
}
ret = pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);//设置线程属性为分离
if(ret!=0){
fprintf(stderr,"pthread_attr_setdetachstate error%s\n",strerror(ret));
exit(1);
}
ret = pthread_create(&tid,&attr,tfn,NULL);
if(ret!=0){
fprintf(stderr,"pthread_creat error%s\n",strerror(ret));
exit(1);
}
ret = pthread_attr_destroy(&attr);
if(ret!=0){
fprintf(stderr,"pthread_attr_destroy error%s\n",strerror(ret));
exit(1);
}
sleep(1);
ret = pthread_join(tid,NULL);
if(ret!=0){
fprintf(stderr,"pthread_join error:%s\n",strerror(ret));
exit(1);
}
printf("main: pid =%d,tid=%lu\n",getpid(),pthread_self());
pthread_exit((void*)0);
}
输出:
thread: pid =14146,tid= 140135779284736
pthread_join error:Invalid argument
线程使用注意事项:
1.主线程退出其他线程不退出,主线程应调用pthread_ exit
2.避免僵尸线程.
pthread_ join。
pthread_ detach.
pthread_ .create 指定分离属性。
被join线程可能在join函数返回前就释放完自己的所有内存资源,所以不应当返回被回收线程线中的值;
3.malloc和mmap申请的内存可以被其他线程释放。
4.应避免在多线程模型中调用fork除非,马上exec,子进程中只有调用fork的线程存在,其他线程在子进程
中均pthread_ exit.
5.信号的复杂语义 很难和多线程共存,应避免在多线程引入信号机制。