线程概念
理解线程
当操作系统开始运行一个进程的时候,也就意味着操作系统会创建一个pcb对我们的进程进行描述,并且会为该进程创建自己的虚拟地址空间。
如下图:
最右边是我们需要执行的任务,我们可以选择串行处理,就是处理完一个再去处理第二个。但是学习了线程之后我们的进程就可以同时处理下面这三个任务。
Linux下没有其实没有线程的具体说法,Linux下的线程是通过轻量化进程实现的,所谓轻量化进程就是一个进程中可以存在多个pcb,这些pcb共用一个虚拟地址空间 每一个线程都是进程中的一个执行流,因此多个执行流可以共同工作,于是也就可以并行完成我们上图所示的任务。
这些所有线程的总和叫做一个线程组,我们也可以将进程理解成就是一个线程组。
- 进程:是一个程序动态的运行,一个程序运行的描述,cpu资源分配的基本单位
- 线程:是一个进程中的执行流,执行程序中的某段代码,cpu执行调度的基本单位
线程之间的独有与共享
独有:
- 线程的标识符:用于区分不同线程
- 寄存器:执行任务时,cpu轮询切换时,保存当前运行相关信息
- 栈:防止调用栈混乱
- 信号屏蔽字:特殊线程执行任务时不希望被某个信号打断
- erron:全局变量errno可以存放错误原因,当错误发生时,函数的返回值是可以通过非法值来提示错误的发生。
- 优先级:线程的调度优先级
共享:
- 虚拟地址空间:代码段,数据段
- 文件描述符表:线程共用一个文件描述符表,进程中文件都可以操作
- 信号的回调函数:当我们自定义之后全局生效
- 用户ID/组ID/工作路径…
之前进程信号的博客说过,信号需要先注销后处理,为了防止被处理多次,这正是防止多线程情况下的时间片轮询所做的。先处理的话,没注销,可能就会被多个线程抢到,从而执行多次。
多线程与多进程的优缺点分析
多线程的优点:
- 共用一个虚拟地址空间,通信更加灵活方便
- 创建销毁的成本更低
- 进程的线程的成本更低
多进程的优点:
- 健壮性,稳定性更高
多线程的优点正是多进程的缺点,多进程的优点也是多线程的缺点。
多进程和多线程在处理,IO密集型程序/cpu密集型程序的时候都会比单一的进程/线程直接处理要块很多。这也是多进程/多线程的应用场所所在。
CPU密集型程序的执行流个数一般是cpu核心数+1,防止切换调度增加成本。
线程控制
线程控制就是对线程的创建》退出》等待》分离进行详细的说明。
前面说过,Linux下实际上是没有线程的概念,线程其实就是轻量化进程,因此下面所说说明的接口是大佬们封装的线程库,线程库提供了一系列操作线程的接口。
线程创建
线程的创建本质上是在内核中创建一个轻量化的进程来实现的。
int pthread_creat(pthread_t *thread,const pthread_attr_t *attr, void*(*start routine)(void*), void *arg);
thread: 输出型参数,获取线程id–线程的操作句柄
attr:线程属性,一般设置成NULL就可以
start routine:函数指针,线程的入口函数
arg:需要传入线程入口函数的参数
成功返回0,失败返回错误编号。
注意:
上面函数的thread参数其实是一个空间的首地址,叫做tid。当线程被创建后,独有的数据就会被描述在共享区,而这个共享区的首地址就是线程的操作句柄,用于操作这个线程。
运行结果:
每个线程都有自己的pid,但是线程创建成功后,通过ps命令仍然只能看见一个pid而这个pid其实叫做tgid,是线程组id,等于主线程id。
只能看到主线程pid
通过ps -efL就可以看到所有线程的pid了(LWP列)。
可以看到有两个pthread_creat.
线程终止
线程的终止就是线程的退出,线程可以异常退出也可以我们自己手动退出,下面我们讲解如何让一个线程退出(正常退出)
void pthread_exit(void* revtal);
作用:退出调用该接口的线程
revtal:保存退出返回值
int pthread_cancel(pthread_t thread);
作用:传入某个线程的操作句柄,从而使该线程被动取消。
参数:需要退出线程的tid
注意:
上面两个函数都是退出线程的,即使退出主线程,其余线程也会继续工作,直到所有线程都退出,但是exit与main中的return 退出的是整个进程。
代码:
执行结果:
可以看到主线程退出后,剩余线程仍然正常工作。
线程等待
线程等待:等待一个线程的退出,获取线程的返回值,并且回收线程所占的资源。
默认情况下一个线程创建出来是需要被等待的,否则就会造成资源的泄露
int pthread_join(pthread_t thread, void **revtal)
thread:等待的指定线程tid
revtal:输出型参数,获取线程返回值
线程分离
线程创建出来默认是需要等待的,但是我们有时候不关心一个线程的返回值,也可以通过设置,让一个线程不用被等待,这就叫做线程的分离。
我们通过将线程的joinable属性修改为detach属性就是线程分离。
属性为detach的线程退出的时候会自己释放资源。
int pthread_detach(pthread_t thread);
当然一个线程也可以自己将自己设置成detach属性,我们通过pthread_self获得自己的tid
int pthread_self(void);
返回值就是自己的tid.