前言
目录
1.Linux下的线程概念
2.Linux线程控制:pthread线程库
在单执行流的进程中,此执行流独占了进程的所有资源
在一个进程内部,有时不一定只有一个执行流,在多执行流下,多个执行流共享了进程的地址空间,我们把“一个程序内部的控制序列”叫做线程
线程本质是在进程的地址空间内运行
进程的切换涉及到页表映射的切换,而线程的切换只是切换了指令序列而在同一个地址空间中进行
那么我们给出下面两个重要概念
- 进程是操作系统分配资源的基本实体
- 线程是进程内部的一个执行流
举个栗子:
在现实生活中,假如我们把社会资源的基本单位看作是家庭
,比如我们经常以家庭年收入统计社会财富的分配状况,那么此时:
- 操作系统—>社会
- 进程—>家庭
- 线程—>家庭成员
家庭成员共享了家庭的资源,家庭成员之间有共享的资源,也有私人的小秘密。
透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流
线程可以被创建、等待、终止、控制
家庭有生老病死、家规等等…
1.Linux下的线程概念
在Linux下,其实没有真正意义上的线程概念,是用进程来模拟的
Linux的进程叫做轻量级进程
LWP是轻量级进程,在Linux下进程是资源分配的基本单位
,线程是cpu调度的基本单位
,而线程使用进程pcb描述实现,操作系统在创建线程时给每个线程都创建一个pcb结构体,并且同一个进程中的所有pcb共用同一个虚拟地址空间,因此相较于传统进程更加的轻量化有了更多执行流之后。进程变成了分配资源的基本实体,进程一旦被创建好之后,里面可能有多个执行流
与进程相比,线程在CPU执行时可能更加轻量化:pcb上下文肯定要切换,但是线程的地址空间、页表不用换;CPU调度时,看到的是LWP,也就是轻量级进程Light Weight Process
1.1 线程的优点
创建
一个新线程的代价要比创建一个新进程小得多- 与进程之间的切换相比,线程之间的
切换
需要OS的工作量更少 - 线程占用的
资源
比进程少得多
1.2 线程能够看到进程的所有资源,因为所有PCB都共享地址空间
- 好处:线程间通信成本低
- 坏处:存在大量的临界资源,势必需要使用各种互斥和同步机制保证临界资源的安全
1.3 线程异常
线程是进程的一个执行分支,当发生野指针/除0等异常操作导致线程退出
的同时,也意味着进程触发了该错误
,操作系统会给进程发送信号,终止进程。这体现了多线程下鲁棒性降低了。
1.4 线程的共享与独享
线程共享
- 文件描述符表
- 每种信号的处理方式
- 工作目录
- 用户id组id
独有:
- 上下文数据(寄存器):体现了多个线程是可以切换的
- 独立的栈结构:体现了线程是独立运行的,各自的上下文数据不会互相影响
2.Linux线程控制:pthread线程库
首先要强调一点:pthread库并不是系统库,而是Linux下为了模拟线程而采用的第三方库。本质是封装了对于轻量级进程的某些操作。
链接这些线程函数库时要使用编译器命令的“-lpthread”选项
接口:
pthread_create()
pthread_join()
pthread_cancel()
pthread_exit()
pthread_self()
2.1线程的创建
功能: 创建一个新的线程
原型
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void * (*start_routine)(void*), void *arg);
4个参数
- thread:返回
线程ID
,这是进程地址空间的共享区的地址
- attr:设置线程的属性,attr为
NULL
表示使用默认属性 - start_routine:是个函数地址,线程启动后要执行的函数
- arg:传给线程启动函数的参数,如果需要传入多个参数,可以用
结构体
封装
返回值: 成功返回0;失败返回错误码
让我们来玩一玩线程的创建,这边我们在main函数,也就是主线程创建了新线程,又在新线程中创建了另外5个更新的线程,使用ps -aL 命令查看轻量级进程
#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;
void* routine(void* args) {
while(true) {
cout << "I am a thread" << endl;
sleep(1);
}
return nullptr;
}
void* ThreadRoute(void* args) {
pthread_t tids[5];
for(int i = 0; i < 5; i++) {
pthread_create(tids+i, nullptr, routine, nullptr);
}
for(int i = 0; i < 5; i++) {