文章目录
- 一、线程的优点
 - 
   
- 1. 创建的代价
 - 2. 切换的代价
 - 
     
- 缓存和进程/线程切换
 
 - 3. 占用的资源
 - 4. 效率
 
 - 二、线程的缺点
 - 
   
- 1. 性能损失
 - 2. 健壮性降低
 - 3. 缺乏访问控制
 - 4. 编程难度高
 
 - 三、线程分离
 - 
   
- 1. 线程分离
 - 2. pthread_detach ()
 - 
     
- ① 函数细节
 - ② 函数使用
 
 
 - 四、线程自有和共享的数据
 - 
   
- 1. 线程自有的数据
 - 2. 线程共享的资源
 
 - 五、多个线程使用公共空间
 - 
   
- 1. 不要把公共空间传给多个线程
 - 
     
- 1.1 问题
 - 1. 2 解决
 
 - 2. 线程的传参和返回值,可以是各种对象
 
 - 六、C++11 中的线程
 - 
   
- 1. C++11 的多线程
 - 2. thread 类及使用
 
 - 七、线程库对线程的管理
 - 
   
- 1. 线程的管理由谁来做?
 - 2. 线程管理的细节
 - 
     
- ① 线程库首先要映射到当前进程的地址空间中
 - ② 进程地址空间中的动态线程库
 - ③ 整个系统的所有的线程都在这一个库中管理着
 
 - 3. 执行流怎么找到栈的?
 
 - 八、线程的封装
 - 
   
- 1. Pthread.hpp
 - 2. test.cc
 
 
一、线程的优点
1. 创建的代价
创建一个新线程的代价要比创建一个新进程小的多。
创建线程只需要创建一个新的 PCB,然后将曾经进程的资源指派给线程即可;而进程创建的时空成本是较高的,有各种数据结构的创建,数据的加载。
2. 切换的代价
与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多。
缓存和进程/线程切换
- 在 
CPU内,每次读取当前进程的代码和数据时,都需要经过虚拟到物理的转换,然后得到内存中的数据放到CPU中做处理,如果这样,那CPU读取任何一条指令,都要访问内存,为了提高运行效率,CPU中存在一种硬件cache(高速缓冲存储器),在CPU进行虚拟到物理的寻址时,本来是找到一行代码,较大概率会执行下一行(当然也可能会跳转到别处),所以会将周边数据全读到CPU内部缓存起来,所以,之后CPU再访问代码数据时,不用再去读取内存了,而是从cache中再读取,大大提高CPU寻址的效率。

 - 进程切换和线程切换: 
  
-  
进程切换:

之前缓存的数据,就全都没有了,会重新加载新进程的数据 -  
线程切换:

cache中的数据在概率上依旧能用,不用重新清空和重新加载。 
 -  
 
所以这才是线程成本低的主要原因。
3. 占用的资源
线程占用的资源要比进程少很多。
进程要占用多执行流,地址空间,页表,代码数据;而线程占用的都是一份的。
4. 效率
- 计算密集型应用:
排序、查找、加密、解密、压缩等动作都以计算为主,是计算密集型应用,主要应用的CPU的资源。 - I/O 密集型应用:
下载、上传、拷贝等就是I/O密集型应用。 
应用要不就是计算密集型,要不就是 I/O 密集型,要不就是两者都是。
- 对于计算密集型应用: 
  
- 为了能在多处理器系统上运行,将计算分解到多个线程中实现。一个线程处理一部分,效率会高。
 - 当然也不是创建的线程越多越好,因为会有切换的成本,切换太多,说不定效率还不如一个进程从头算到尾。
 - 所以,对于计算密集型,一般是 
CPU有多少个核就创建几个。
可以通过lscpu查看一下核数:
 
 - 对于I/O 密集型应用: 
  
- 可以多创建进程,因为 
I/O过程大部分都在等,一个进程等10G的数据,和10个线程每个等1G的数据,效率不一样。 
 - 可以多创建进程,因为 
 
二、线程的缺点
1. 性能损失
多线程切换有成本,像上面的计算密集型应用,线程过多可能会有较大的性能损失(增加了额外的同步和调度开销,而可用资源不变)。
2. 健壮性降低
一个线程出问题,整个进程就挂掉了(这一点,线程 - 线程退出 中有讲到)。
 在一个多线程程序里,因共享了不该共享的变量而造成不良影响的可能性很大。
3. 缺乏访问控制
线程间共享资源,对于共享资源,大家都可以任意时间访问,可能会有同时访问同一量的情况,可能会出错。
4. 编程难度高
编写与调试一个多线程程序比单线程程序要困难很多,往往需要全面深入的考虑以保证程序的健壮性。
三、线程分离
1. 线程分离
线程默认是 joinable(可联接) 的,如果主线程不关心新线程的执行信息,可以将新线程设置为分离状态。
 当线程处于分离状态后,退出时会自动释放线程资源。
 
- 分离和等待:
线程被分离后,就不能被pthread_join()了,调用这个函数,函数就会返回一个错误码。 - 分离状态:
‘分离’仅仅是一种状态,一种不用主线程等的状态,而不是真正分离了。在这种状态下,线程出异常了,进程会受影响退出;主线程执行完了,进程退出,线程也会退出。 
不管是分离还是等待,我们都希望主线程是最后退出的,所以分离的一个场景,就是主进程根本就不退出。
 大部分程序都是一直在运行的,称为常驻进程。
2. pthread_detach ()
① 函数细节

thread参数:要分离的线程的ID。- 返回值:成功返回 
0,失败返回错误码 
② 函数使用
- 主线程分离新线程:
 
#include <pthread.h>
#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
using std::cout;
using std::endl;
void *thfunc(void *arg)
{
   
    while (1)
    {
   
        sleep(1);
        cout << "new" << endl;
    }
    return nullptr;
}
int main()
{
   
    pthread_t rid;
    pthread_create(&rid, nullptr, thfunc, nullptr);
    pthread_detach(rid);
    sleep(3);
    cout << "man" << endl;
    return 0;
}
 
运行结果:
 
- 新线程自己分离自己:
 
void *thfunc(void *arg)
{
   
    pthread_detach(pthread_self());
    while (1)
    {
   
        sleep(1);
        cout << "new" << endl;
    }
    return nullptr;
}
int main()
{
   
    pthread_t rid;
    pthread_create(&rid, nullptr, thfunc, nullptr);
    sleep(3);
    cout << "man" << endl;
    return 0;
}
 
运行结果:
 
- 在线程分离后,再通过 
pthread_join()等待线程会等待失败,函数会返回一个错误码:

 
四、线程自有和共享的数据
进程是资源分配的基本单位,线程是调度的基本单位。进程强调独立,线程强调共享。所以我们来看一下线程自有的和共享的部分。
1. 线程自有的数据
- 线程 ID: 是用户级别的,内核级的不使用线程 
ID,使用的是线程的LWD。 - 硬件上下文: 每个线程都是被单独调度的执行流,所以要有自己的上下文数据,存放在一组寄存器中。
 - 独立栈结构: 线程本质是在执行自己的函数,每个函数内都可以定义各种临时变量,临时变量都是存放在栈上的。每个线程都有独立的用户栈,不敢让它们一起用一块栈空间。
 
除了上面几个较为重要的,还有 errno、信号屏蔽字、调度优先级等。
2. 线程共享的资源
- 地址空间
 - 文件描述符表:因为文件描述符表表示的是进程和打开文件的关系,而不是线程。
 - 每种信号共享的处理方式:这就是为什么一个线程出异常,整个进程就崩掉了。
 - 当前工作目录:进程在哪里,线程就在哪里。
 - 用户 
id和组id。 
五、多个线程使用公共空间
1. 不要把公共空间传给多个线程
我们来看下面这段代码:
 我们想一次性创建 6 个线程,然后每一个线程都在自己的线程函数中打印一下自己的线程名字(从 1 到 6 号)。
#include <pthread.h>
#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
using std::cout;
using std::endl;
const int threadsnum =
                ![[数据集][目标检测]无人机飞鸟检测数据集VOC+YOLO格式6647张2类别](https://i-blog.csdnimg.cn/direct/fd65f0dba85840809e73bcd4dd546bbf.png)

















