线程(一)

发布于:2024-03-04 ⋅ 阅读:(65) ⋅ 点赞:(0)

🔶线程
 🔶makefile文件编写
 🔱线程接口介绍
  🔱创建线程——pthread_create
   🔶补充pthread_self——获取本线程id
  🔱线程退出——pthread_exit,pthread_cancel
  🔱线程回收——pthread_join
  🔱线程分离和joinable状态
 🔱线程退出+回收的思维导图
 🔱调用pthread库的执行过程
 🔱pthread库
 🔱局部存储——__thread编译选项
🔱错误总结
 🔱线程切换比进程切换效率高

在这里插入图片描述

🍑线程

🍉makefile文件编写

  1. g++ -pthread -o $@ $^ -std=c++11
  2. g++ -o $@ $^ -std=c++11 -lpthread
    两个都是对的,任选一个
    因为pthread是一个动态库,对于动态库我们需要告诉编一个库的名字,位置
    如果不加就会找不到动态库

🍑线程接口介绍

🍐创建线程——pthread_create

在这里插入图片描述

功能:创建一个新的线程
参数:thread:返回线程ID ,pthread_t类型
attr:设置线程的属性,attr为NULL表示使用默认属性
start_routine:是个函数地址,线程启动后要执行的函数
arg:传给线程启动函数的参数
返回值:成功返回0;失败返回错误码

传递的参数为void*类型,表示可以传递任意类型的参数

🍐补充pthread_self——获取本线程id

在这里插入图片描述

我们以传递结构体为例

#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<vector>
#include<functional>
#include<stdio.h>
#include<time.h>
using namespace std;

struct Thread
{
    Thread(string name="thread_1",pthread_t id=0):_name(name),_id(id){}
    string _name;
    pthread_t _id;
};
void* fun(void* t)
{
    ((Thread*)t)->_id = pthread_self();
    return nullptr;
}
int main()
{
    pthread_t id;
    Thread t("thread_1");
    pthread_create(&id,nullptr,fun,&t);
    sleep(1);
    cout<<t._name<<" -> " << t._id<<endl;
    return 0;
}

在这里插入图片描述
主线程想获取到子线程的id必须等一等

🍉线程退出——pthread_exit,pthread_cancel

线程退出不能使用exit退出,exit是进程退出

  1. return,但是这种方式对主线程不适用,主线程使用return相当于exit
  2. pthread_exit
  3. pthread_cancel——同一个进程A线程终止B线程
    不可以自己终止自己使用pthread_cancel,不能子线程cancel父线程,后果自负

在这里插入图片描述
同时线程退出也是会有类似进程僵尸的情况发生的,需要对线程的退出进行管理

🍉线程回收——pthread_join

在这里插入图片描述

🍉线程分离和joinable状态

在这里插入图片描述
分离可以在主线程也可以在自己线程结束之前
线程默认为join able状态
分离态就是子线程脱离父线程的管控不需要父线程回收资源可以使用cancel结束线程


🍉线程退出+回收的思维导图

在这里插入图片描述

🍐调用pthread库的执行过程

请添加图片描述

  1. 从磁盘加载代码到物理内存
  2. 将物理内存映射如地址空间
  3. 在运行到pthread库函数的时候跳到共享存储区中找相应的地址执行代码
  4. 执行完后跳回代码段继续执行

🍉pthread库

请添加图片描述
pthread封装了系统调用,在库中对内存和数据结构进行了管理

在这里插入图片描述
原本的虚拟地址空间——主线程使用
库中每个子线程都有一个空间id对应的是虚拟地址,并且每个空间都有自己的,并且库中还有相应的库函数
所以在cancel的时候传入的是id——id存的是地址,直接通过地址去找需要结束的子线程,更改数据空间就可以了

🍐局部存储——__thread编译选项

#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<vector>
#include<functional>
#include<stdio.h>
#include<time.h>
using namespace std;

__thread int cnt = 100; // 表示每个线程都有一个变量存储在各自的栈中
void* fun(void* t)
{
    while(1)
    {
        cnt--;
        cout<<"thread_1 " << cnt<<endl;
        sleep(1);
    }
    return nullptr;
}
int main()
{
    pthread_t id;
    pthread_create(&id,nullptr,fun,nullptr);
    while(cnt)
    {
        cout<<"main thread " << cnt<<endl;
        sleep(1);
    }
    return 0;
}

在这里插入图片描述

__thread —— 每个线程都有一个变量存储在各自的栈中
__thread只能对内置类型使用,不能对自定义类型使用(vector/string…)


🍑错误总结

  1. 多个线程各自有一个栈,共有一个堆
  2. 线程包含cpu现场,但是线程只是进程中的一个执行流,执行的是程序中的一个片段代码,多个线程完整整体程序的运行
  3. 线程的粒度小于进程,占用资源更少,因此通常多线程比多进程并发性更高

🍉线程切换比进程切换效率高

  1. 切换的寄存器少
  2. 线程会将代码附近代码加载进cpu的cache中,在进行线程切换的时候有较大概率使用cache中的代码;但是在进程切换的时候,加载进cache中的代码就失效了,需要重新加载