再学习完Linux进程后,本期,我们来讲解Linux线程
1.为什么需要线程
在之前学习进程前,我们写的所有代码几乎都是单个执行流的,也就是说我们的代码只有一条路走.
在学习进程后,我们可以通过fork进行进程创建,给进程分配任务进行多执行流执行任务,问题来了
那我们为什么还需要线程呢
1.线程是什么,线程的特性(在Linux下)
1.相对于进程与进程之间,一个进程下的线程与线程之间都是共享该进程的大部分地址空间,也就共 享了大部分进程数据
2.一切进程至少都有一个执行线程
3.线程在进程内部运行,本质是在进程地址空间内运行
4.在Linux系统中,在CPU眼中,看到的都是进程,PCB都要比传统的进程更加轻量化
5.透过进程虚拟地址空间,线程之间可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流
1.对比windows
在Linux下,单叫线程可能并不准确,我们更愿意称之为轻量级进程,相对于Windows而言,Linux的线程就优雅许多
Windows:对于线程的管理是单独额外管理,明确的区分线程和进程,对线程也就单独组织,进程中再用指针指向组织线程的地址.
Linux:线程之间共享进程地址空间,在一个程序里的一个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的控制序列”,也就是轻量级进程,而轻量级进程之间会共享进程地址空间
所以也就相当于Linux中是没有线程的,只存在轻量级进程,线程只是用户级的说法
2.线程(轻量级进程) 轻量在什么地方
由于在Linux下线程为成为轻量级进程更为恰当,下文我就直接称为轻量级进程了
首先,对于cpu来说,它看到的都是进程,轻量级进程也就是一个更加轻量化的task_struct,他们和进程并无巨大差别,轻量级进程会轻量在以下地方
-
进程地址空间: 轻量级进程与其所属进程共享相同的地址空间。这意味着它们可以访问相同的内存区域,包括代码段、数据段等。
-
内核数据结构: 轻量级进程在内核中通常表示为与父进程相同的数据结构,只是某些字段可能会有所不同,例如栈指针、指令指针等。
-
文件描述符: 轻量级进程与其父进程共享相同的文件描述符表。这意味着它们可以访问相同的文件、管道、套接字等。
-
进程控制块(PCB): 轻量级进程的 PCB 通常与父进程的 PCB 共享很多信息,例如进程 ID、进程状态等。
需要注意的是,每一个轻量级进程都会有自己的独立栈(用户栈),这样轻量级进程之间即使调用函数也不会互相干扰.
3.线程间切换
由于在Linux中线程之间是共享进程地址空间的,而在cpu中,有一块cache缓存,然后cpu中的寄存器会从cache缓存拿数据,它会缓存进程的上下文代码数据,大概一般都会有几十 KB 到几 MB左右(还挺大的),所以再进行线程切换的时候,线程不需要再重新让cache缓存重新加载新的代码数据,这样就不需要冷加载,线程间切换效率也会大大提升
Linux下线程的相关函数
1.pthread_t
创建一个线程单独的线程tid
2. pthread_create
功能:创建一个线程
参数
thread:返回线程ID
attr:设置线程的属性,attr为NULL表示使用默认属性
start_routine:是个函数地址,线程启动后要执行的函数
arg:传给线程启动函数的参数
返回值:成功返回0;失败返回错误码
3.pthread_join
功能:用于等待一个线程结束
参数:
thread:线程ID
retval:拿到线程退出码(这里是二级指针哦)
4.pthread_exit
功能:用于线程自己退出
参数
retval:retval不要指向一个局部变量。
返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)
使用示例:
#include <iostream>
#include <unistd.h>
#include <pthread.h>
using namespace std;
void *func1(void *args)
{
string threadname = (char *)args;
cout << threadname << endl;
int cnt = 5;
while (cnt--)
{
sleep(1);
cout <<"I'm thread pid::"<< getpid() << endl;
}
// return (void*)123;
pthread_exit((void *)123); // 线程退出
}
int main()
{
pthread_t tid;
pthread_create(&tid, nullptr, func1, (void *)"pthread1");
int cnt = 10;
while (cnt--)
{
cout <<"I'm main pid::"<< getpid() << endl;
sleep(1);
}
// 主线程
void *ret = nullptr; // 拿到线程退出码
int n = pthread_join(tid, &ret);
std::cout << "main thread quit, n=" << n << " main thread get a ret: " << (long long)ret << std::endl; // void*8个字节
return 0;
}