线程与进程有许多相似的地方,也有很多不同的地方,最好将他们对比学习
一、线程的概念
1.1线程的定义
线程是在进程内部执行的执行流,进程就是只有一个执行流的线程,也是操作系统最小操作单位 。
1.2线程与进程的不同
1.进程间有亲缘关系,线程创建的主线程和新线程之间都是对等的。
2.线程是在进程的地址空间内运行的,所以创建一个进程除了创建pcb还要创建资源(虚拟地址空间,页表、内存中的代码和数据也要开辟空间并加载);创建线程只需要创建pcb并分配资源即可。
3.线程比进程的执行粒度更小。
4.线程和线程共享全局变量、代码段、数据段、文件描述符表(第一个进程中打开的文件,后面创建的进程也可使用),但各自拥有独立的线程ID、上下文(被切换时)、栈(保存临时变量)、寄存器、优先级。
因为线程中很多资源是同一个进程中的线程间共享的所以进程也被叫做轻量级进程,这样不论是进程还是线程Linux操作系统都可以把他看做进程进行操作。
进程是分配资源的基本单位,资源包括地址空间要分配、内存要分配页表要分配、数据要加载、映射关系要维护进程与文件之间的关系的构建,进程中有一个执行流,就创建一个pcb,多个执行流就创建多个pcb。
CPU的角度只关心pcb,调度的基本单位就是pcb,程序执行的基本单位是线程。
二、进程与线程的优缺点对比
优点:
1.调度时切换的成本更小
调度时进程间切换栈空间代码段数据段全部都要切换,线程间切换就只需要线程私有的部分
2.线程占用的资源比进程小(共享的部分多)
3.充分利用CPU多处理器的可并行数量,如计算密集型应用
4.等待磁盘IO网络IO等慢速IO时程序可以执行其他的计算任务
缺点:
1.性能损失:线程切换时也是性能损失。
2.资源是共享是临界资源,缺乏访问控制
3.线程错误时终止整个进程。其他的线程的资源都被回收了,算是无辜受害吧
三、线程的基本操作
多线程编程有很多线程库,本文所使用的POSIX线程库只是其中一种。
创建操作:
int pthread_create(pthread_t *thread,//线程标识符输出型参数,通过这个参数返回线程id
const pthread_attr_t *attr,//属性NULL
void (start_routine) (void *),//线程回调函数
void *arg);//线程处理函数的参数
成功时返回0失败时返回错误码
创建成功后新线程和主线程的执行顺序不一定(与fork相同)由调度器决定。
1#include <stdio.h>
2 #include <pthread.h>
3 #include <unistd.h>
4 #include <errno.h>
5 #include <string.h>
6 #include <stdlib.h>
7 void *route (void *arg)
8 {
9 printf("thread one");
10 sleep(1);
11 }
12 int main()
13 {
14 pthread_t tid;
15 int ret = pthread_create(&tid,NULL,route,NULL);
16 if(ret != 0)
17 {
18 fprintf(stderr,"create %s\n",strerror(errno));
19 exit(0);
20 }
21 while(1)
22 {
23 printf("main thread\n");
24 }
25 printf("main thread \n");
26 return 0;
27 }
查看线程的指令:
其中LWP就是(线程)轻量级进程的线程id,可以看到两个进程组idPID相同,但LWP不同。
Linux中的gettid的系统调用返回其线程id,但c库并没有将该系统调用封装起来、开放接口供人使用。使用时就需要
#include <sys/syscall.h>
pid_t tid;
tid = syscall(SYS_gettid);
在用户态中主进程的id和进程组的id相同,因为内核在创建第一个线程时会将线程组的id设置成第一个线程的线程id。在内核中主线程被称为group_leader,group_leader指针指向主线程的进程描述符。
获得线程标识符pid_t gettid(void) 返回的是线程栈上的地址,不是task_struct中的pid
pthread_t pthread_self(void);获得线程自身的id
线程等待:
如果主线程不等待已经推出的线程其空间没有释放,仍然在进程的地址空间中,创建新的线程不会复用刚才退出线程的地址空间会造成资源的浪费进而造成内存泄漏。
int pthread_join(pthread_t thread,//线程ID
void **retval)//输出型参数,获得所等线程的退出信息
线程退出:
线程退出时,只有可能是代码跑完结果正确,代码跑完结果不正确。调度器决定哪个线程先执行同进程,主线程等新线程,主线程后结束
void pthread_exit(void *retval);主线程要用进程的方式退出return或者exit();
线程分离:
如果不关⼼线程的返回值,join是⼀种负担,这个时候,我们可以告诉系统,当线程退出时,⾃动
释放线程资源
默认创建的线程都是可结合的,线程分离后就可不等待,线程退出系统系统自动回收
可以自己分离自己,也可通过线程tid分离
int pthread_detach(pthread_t thread);