目录
线程概念:
操作系统分配资源以进程为基本单位。而线程是进程的组成部分,它代表了一条顺序的执行流,每个进程至少有一个执行流。线程是cpu调度的最小单位,同类的多个线程共享同一块虚拟地址空间和页表。假如有一个程序它由fun1()fun2()fun3()三个函数组成。该程序运行起来后,操作系统为他分配相应的资源,pcb块,虚拟地址空间,页表等如下图所示。
这样会按顺序执行 fun1() fun2() fun3(),但是如果想并发执行 这三个函数,提高效率时。可以对每个函数建立一个进程并发执行,同时也要创建每个进程的虚拟地址空间与页表, 这样在程序运行时会对性能造成不小的影响 .。而线程就是在创建出PCB的时候让这些PCB共用同一块虚拟地址空间,页表等资源,但是程序计数器,上下文数据不一样,从而去执行不同的函数,这样就大大提高了性能,如下图所示:
总结:
在linux中没有线程的概念,线程的创建是通过库函数实现的,库函数通过是模拟进程pcb实现的(如上图所示),所以linux下线程又称为轻量级进程。在传统意义上pcb是一个进程控制块,而在linux中pcb只能算一个线程控制块。进程就是多个线程的集合,每个线程是一个执行流,共享进程的大部分资源。
线程之间的独有与共享:
独有:标识符,寄存器,栈,信号屏蔽字, errno,优先级。
共享:虚拟地址空间(代码段/数据段) ,文件描述符表,信号处理的回调函数,用户ID/组ID/工作路径等
线程控制:
创建线程:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
头文件:#include <pthread.h>
在编译时注意加上-lpthread参数,以调用静态链接库。因为pthread并非Linux系统的默认库
参数:
thread: 输出型参数,指向线程标识符的指针。
attr: 用来设置现成的属性。
start_routine:函数指针,传入线程运行函数的起始地址。注意函数指针的类型必须是void* (*)(void*) 。既:函数返回值,参数的类型都是 void*
arg:线程运行函数的参数。
返回值:创建线程成功时,返回0,创建线程失败,返回错误号
练习程序:
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
void* thr_start(void* ptr){
while(1){
printf("i am thread %s \n",(char*)ptr);
sleep(1);
}
return NULL;
}
int main(){
char* ptr = "你好";
pthread_t tid;
int res = pthread_create(&tid, NULL, thr_start,(void*) ptr);//tid,线程属性设置 函数运行起始地址 函数参数
if(res != 0){
printf("线程创建失败");
return -1;
}
while(1){
printf("i am main thread \n");//主线程的无限次循环
sleep(1);
}
return 0;//退出进程 而非主线程。
}
程序这个程序主要展示线程的创建流程。主线程:创建一个线程,然后运行一个无限循环函数,创建的函数也是是一个无限循环函数。运行结果如下:
可以看见两个线程并行运行:此时通过命令(ps -efL | head -n 1 && ps -efL | grep pthread_create)查看进程的运行状态可以查看到两个./pthread_create
他们的PID都一样是12467,LWP是指轻量级进程id分别是 12467 和 12468。主线程的轻量级进程id和进程id一样。
线程退出:
(1)
线程入口函数运行完毕,线程就会自动退出--在线程入口函数中调用return (但是main函数中return,退出的是进程而不是主线程)。
(2)
void pthread_exit(void *retval);
那个线程调用就退出哪个线程。如果是主线程调用,主线程退出。但是主线程退出并不会导致进程退出,只有所有的线程都退出了,进程才会退出。
参数:线程的退出返回值。
(3)
int pthread_cancel(pthread t thread);
根据线程tid,终止一个指定的线程;退出的线程是被动取消的
线程等待:
线程等待:等待一个线程的退出,获取退出线程的返回值,回收线程所占的资源
线程有一个属性,默认创建出来这个属性是joinable,处于这个属性的线程,退出后,需要被其它线程等待获取返回值回收资源。int pthread join(pthread t thread, void *retval); --阻塞等待指定线程退出,获取其返回值
int pthread_join(pthread_t thread, void **retval);
thread:要等待退出的线程tid --阻塞函数,线程没有退出则一直等待。
retval:输出型参数,用于返回线程的返回值
线程的返回值是一个void*,是一个一级指针 若要通过一个函数的参数获取一级指针,就需要传入一个一级指针变量的地址进来。
默认情况下,一个线程是必须被等待,若不等待,则会造成资源泄漏
练习程序:
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
void* thr_start(void* ptr){
//睡两秒退出线程
sleep(2);
printf("这里是创建的线程\n");
char* ptr = "这是创建线程的返回值";
pthread_exit((void*)ptr);
}
int main(){
pthread_t tid;
int res = pthread_create(&tid, NULL, thr_start, NULL);
if(res != 0){
printf("线程创建失败\n");
return -1;//进程退出
}
char* ptr;
pthread_join(tid,(void**)&ptr);
printf("%s\n",(char*)ptr);
return 0;
}
运行结果: