linux|多线程(一)

主要介绍了为什么要有线程 和线程的调用 和简单的对线程进行封装。

背景知识

a.重谈地址空间

我们知道物理内存的最小单元大小是4kB
物理内存是4G那么这样的单元友1M个
操作系统先描述再组织struct page[1M]
对于32位数据字长的机器,页表有2^32条也就是4G条,每一条光保存两个地址加一个标记位 这个页表就得有36G这样大
实际上比如 0010 0010 0010 0010 0010 0010 0010 0010
在这里插入图片描述
虚拟地址>> 12 得到 页框号
虚拟地址&&oxfff 得到 页内偏移量

线程的概念

线程:在进程内部运行,是cpu调度的基本单位
进程承担系统资源的基本实体
在这里插入图片描述
进程承担系统资源的基本实体 怎么理解这句话呢?可以把进程看作家庭,国家的最小单位国是千万家~~家是社会资源分配的最小单位,把线程看作个人,线程是进程的一部分

os要单独设计线程 --新建?暂停?销毁?调度?线程要不要和进程产生关联

在windows 有 专门管理线程的数据结构

struct tcb
{
// 线程id
// 线程 优先级
// 状态上下文
// 链接属性
};

在这里插入图片描述
但是linux 中没有专门的线程,Linux 是用进程模拟的线程
linux中的执行流,我们称为轻量级进程
线程:在进程内部运行,是cpu调度的基本单位
在cpu看来被调用的都是轻量级进程

澄清进程和线程

进程 是 只有一个执行流的线程~

见一见线程

在这里插入图片描述
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);

pthread_t *thread: 一个指向pthread类型的指针 , pthread 这个类型用于保存的线程的标识符
const pthread_attr_t *attr: 线程属性对象的引用,可以用来设置线程的优先级、调度策略等。如果不需要设置特殊属性,可以传入 NULL 使用默认属性。
start_routine: 指向线程开始执行的函数的指针这个函数的类型应为 void ()(void*),意味着它接受一个 void * 类型的参数,并返回 void * 类型的结果。
arg: 传递给 start_routine 函数的参数,类型为 void *。这使得你可以在创建线程时向线程函数传递数据。

#include <iostream>
#include <unistd.h>void *threadStart(void * args)
{while(true){std::cout<<"new thraed running..."<<",pid:"<<getpid()<<std::endl;sleep(1);}
}int main()
{pthread_t tid;// 新线程pthread_create(&tid, nullptr, threadStart,(void *) "thread-new"); // 相当于把页表分成了两部分// 主线程while(true){sleep(1);std::cout<<"main thraed running..."<<",pid:"<<getpid()<<std::endl;}return 0;
}

有了多进程为什么还要有多线程

线程不用再创建页表等,只需要一个pcb 创建的成本低
运行期间线程,的调度成本低,因为不需要切换页表
删除线程,比删除一个进程的成本低,只需要删除一个pcb就行了

线程的调度成本为什么更低呢?

我们切换页表只需要修改对应寄存器中的值就可以了,这个不是主要原因
最主要的原因是
在这里插入图片描述
数据会被缓存在cache中,一切换进程,cache中的数据就作废了,就需要重新cache了

为什么还要有进程呢?

线程的健壮性比较低,一个线程出错整个程序都退出了

哪些东西在多线程中是共享的

因为同一地址空间,所以代码段,数据段都是共享的,文件描述符表,每种信号的处理方式,工作目录。

哪些东西是各自都有一份的呢?

线程id,错误码,调度优先级,调度的上下文,信号屏蔽字
**最重要的:**一组寄存器,保存硬件上下文数据 线程是在调度运行的
栈:线程的栈主要用于存储函数调用的局部变量、函数参数以及返回地址。每当线程调用一个新的函数时,相关的局部变量和函数参数就会被压入该线程的栈中,形成一个新的栈帧。当函数返回时,相应的栈帧就会被弹出,恢复之前的调用环境。由于每个线程有独立的栈,它们可以并发地进行函数调用,而不会相互干扰。

几个基本的问题

问题1:主线程和新线程谁先运行?

系统会将这个新线程加入到就绪队列中等待调度。调度器会根据其自身的算法(比如时间片轮转、优先级等)来决定哪个线程获得 CPU 时间片并开始执行。

问题2:我们期望谁最后退出? main thread,你如何保证呢?

我们希望主线程后退出,如果主线程先退出,主线程退出了整个进程就结束了,可能此时新线程还没有完成任务呢。
我们用pthread_join 来保证 主线程后退出

void * RunThread(void * args)
{std:: string name;name = (const char *)args;std::cout <<name << std::endl;return args;
}
int main()
{pthread_t tid;pthread_create(&tid, nullptr, RunThread,(void *)"thread - 1");int n = pthread_join(tid,nullptr);if(n == 0){std::cout<<"wait success"<<std::endl;}return 0;
}

在这里插入图片描述

问题3: tid 是什么样子的?是什么呢?虚拟地址! 为什么?

void  PrintToHEX(pthread_t tid)
{char message[1024];snprintf(message,sizeof(message),"0x%lx",tid);std::cout<<message<<std::endl;
}

我们打出来发现是一个地址
在这里插入图片描述
为什么是一个地址呢?我们等下再说

问题4:全面看待线程函数传参: 我们可以传递任意类型,但你一定要能想得起来,也可以传递类对象的地址!!!

class ThreadData
{
public:int x;int y;
};
void * RunThread(void * args)
{std:: string name;ThreadData * data = (ThreadData *)args;std::cout <<"x:"<<data->x <<" y:"<<data->y<< std::endl;return args;
}
void  PrintToHEX(pthread_t tid)
{char message[1024];snprintf(message,sizeof(message),"0x%lx",tid);std::cout<<message<<std::endl;
}
int main()
{pthread_t tid1;ThreadData x1;x1.x = 10;x1.y =20;pthread_create(&tid1, nullptr, RunThread,(void *)&x1);// PrintToHEX(tid);int n = pthread_join(tid1,nullptr);if(n == 0){std::cout<<"wait success"<<std::endl;}return 0;
}

问题6:注意刚刚我们x1的写法其实是不标准的 为什么?

#include <iostream>
#include <pthread.h>
#include <string>
class ThreadData
{
public:int x;int y;
};
void * RunThread1(void * args)
{std:: string name;ThreadData * data = (ThreadData *)args;data->x = 5;data -> y = 10;std::cout <<"x:"<<data->x <<" y:"<<data->y<< std::endl;return args;
}
void * RunThread(void * args)
{std:: string name;ThreadData * data = (ThreadData *)args;std::cout <<"x:"<<data->x <<" y:"<<data->y<< std::endl;return args;
}
void  PrintToHEX(pthread_t tid)
{char message[1024];snprintf(message,sizeof(message),"0x%lx",tid);std::cout<<message<<std::endl;
}
int main()
{pthread_t tid1;ThreadData x1;x1.x = 10;x1.y =20;pthread_create(&tid1, nullptr, RunThread,(void *)&x1);pthread_t tid2;pthread_create(&tid2, nullptr, RunThread1,(void *)&x1);int n = pthread_join(tid1,nullptr);if(n == 0){std::cout<<"wait success"<<std::endl;}return 0;
}

比如说这样,我们原本想要打印 10 ,20 5,10 的 结果却这样。
在这里插入图片描述
因为main 栈上的变量 这两个线程都可以看到并修改,且这个变量只有一份,当第一个线程正准备打印的时候,第二个线程修改了这两个变量的值。导致第一个线程打印出来就和第二个线程一样了。 根本原因就是 变量只有一份,我们多创建一份就好了。

而且我们不推荐直接 将main栈上的变量给新线程因为 main函数栈的变量生命周期是随main函数的,可能新线程早都结束了,但是main栈上的变量就是不结束
我们用new 可以在新线程中方便管理 传过来的变量的生命周期。

问题5: 全面看待线程函数返回

它也可以传递自定义类型哦

#include <iostream>
#include <pthread.h>
#include <string>
class ThreadData
{
public:int x;int y;int Excute(){return x + y; }
};
class ThreadResult
{
public:int x;int y;int ans;void print(){std::cout<<std::to_string(x)+"+"+std::to_string(y)+"="+std::to_string(ans)<<std::endl;}
};void * RunThread(void * args)
{std:: string name;ThreadData * data = (ThreadData *)args;//std::cout <<"x:"<<data->x <<" y:"<<data->y<< std::endl;ThreadResult * result = new ThreadResult();result->ans = data->Excute();result->x = data->x;result->y = data->y;return (void*)result;
}
void  PrintToHEX(pthread_t tid)
{char message[1024];snprintf(message,sizeof(message),"0x%lx",tid);std::cout<<message<<std::endl;
}
int main()
{pthread_t tid1;ThreadData * x1 = new ThreadData();x1->x = 10;x1->y = 20;pthread_create(&tid1, nullptr, RunThread,(void *)x1);ThreadResult * result =nullptr;int n = pthread_join(tid1,(void**)&result);result->print();if(n == 0){std::cout<<"wait success"<<std::endl;}return 0;
}

问题6: 如何创建多线程呢?

我们通过循环的方式 创建多线程哦~~

#include <iostream>
#include <pthread.h>
#include <string>
#include <unistd.h>
void * RunThread(void * args)
{std::string name = (const char *) args;std::cout<<name<<std::endl;return args;
}
int main()
{for(int i = 0; i < 10; i++){pthread_t tid;char name[1024];snprintf(name,sizeof(name),"this is %d new thread",i+1);pthread_create(&tid, nullptr , RunThread,name);}sleep(100);return 0;
}

在这里插入图片描述
我们发现结果并不符合预期。 因为name 只有一份,主线程 运行的时候不停在覆盖 name。
我们用 new ,循环 new 十次 主线程分配的时候每一个线程都得到 一个地址,那个地址指向一个独立的堆空间,线程之间就不互相影响了。
在这里插入图片描述
ok 完美解决~~!!!

问题7: 新线程如何终止?

1.我们可以在新线程中用return
2.我们可以在新线程中用pthread_exit 注意exit 是终止整个进程。
3.我们可以在新线程中使用pthrad_cancel
线程被取消线程的退出结果是:-1 #define PTHREAD_CANCELED ((void *) -1)

#include <iostream>
#include <pthread.h>
#include <string>
#include <unistd.h>
#include <vector>void *RunThread(void *args)
{while (true){std::string name = (const char *)args;std::cout << name << std::endl;sleep(3);}return args;
}int main()
{std::vector<pthread_t> tids;for (int i = 0; i < 10; i++){pthread_t tid;// char name[1024];char *name = new char[1024];snprintf(name, 1024, "this is %d new thread", i + 1);pthread_create(&tid, nullptr, RunThread, name);tids.push_back(tid);}for (auto tid : tids){// pthread_detach(tid);pthread_cancel(tid);void *ret = nullptr;int n = pthread_join(tid, &ret);std::cout << (long long int)ret << " quit.." << std::endl;// std::cout<<"n="<<n<<std::endl;}return 0;
}

在这里插入图片描述

pthread_cancel 是请求取消另一个线程,而 pthread_exit 是线程自身决定退出。

在这里插入图片描述
在这里插入图片描述
某个线程调用了exit 整个进程就结束了~
exit 你走错片场了啊。要用pthread_exit

void * RunThread(void * args)
{std::string name = (const char *) args;std::cout<<name<<std::endl;pthread_exit(args);return args;
}

在这里插入图片描述

问题8: 可以不可以不join线程,让他执行完就退出呢??可以!

我们用pthread_detach 就可以了,然后线程变成unjoinable 状态,如果此时再等待就会出错 然后终止整个进程。

#include <iostream>
#include <pthread.h>
#include <string>
#include <unistd.h>
#include <vector>void *RunThread(void *args)
{while (true){std::string name = (const char *)args;std::cout << name << std::endl;sleep(3);}return args;
}int main()
{std::vector<pthread_t> tids;for (int i = 0; i < 10; i++){pthread_t tid;// char name[1024];char *name = new char[1024];snprintf(name, 1024, "this is %d new thread", i + 1);pthread_create(&tid, nullptr, RunThread, name);tids.push_back(tid);}for (auto tid : tids){pthread_detach(tid); // 主线程分离新线程,新线程必须存在}for (auto tid : tids){// pthread_detach(tid);pthread_cancel(tid);void *ret = nullptr;int n = pthread_join(tid, &ret);std::cout << "n:"<<n<<std::endl;// std::cout<<"n="<<n<<std::endl;}return 0;
}

在这里插入图片描述

封装线程

#include <string>
#include <pthread.h>
#include <functional>
namespace ThreadModel
{   class Thread{typedef void  (*func_t)(std::string); // ?// using func_t = std::function<void()>;public:Thread(const std::string & name,func_t func):_name(name),_func(func){}~Thread(){}void Excute(){_isrunning = true;_func(_name); // 这里传了线程名_isrunning = false;}static void * Routine(void * args) // 不用static 第一个参数是this{Thread * self  = static_cast<Thread*>(args);//  self->_func();self->Excute();return nullptr;}void start(){pthread_create(&_tid,nullptr,Routine,(void *)this);}void join(){// std::cout<<_isrunning<<std::endl;//  std::cout<<true<<std::endl;if(_isrunning) // 只有在运行中的线程才需要被等待{//std::cout<<"..."<<std::endl;pthread_join(_tid,nullptr);}}// 终止线程void stop(){if(_isrunning){_isrunning = false;pthread_cancel(_tid);}}private:std::string _name;pthread_t _tid;func_t _func; bool _isrunning;  // 主要 用于线程终止时 只有启动了的线程 才能终止      };
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/46178.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LabVIEW Communications LTE Application Framework 读书笔记

目录 硬件要求一台设备2台USRPUSRP-2974 示例项目的组件文件夹结构DL Host.gcompeNodeB Host.gcompUE Host.gcompBuildsCommonUSRP RIOLTE 操作模式DLeNodeBUE 项目组件单机双机UDP readUDP writeMAC TXMAC RXDL TX PHYDL RX PHYUL TX PHYUL RX PHYSINR calculationRate adapta…

R语言优雅的把数据基线表(表一)导出到word

基线表&#xff08;Baseline Table&#xff09;是医学研究中常用的一种数据表格&#xff0c;用于在研究开始时呈现参与者的初始特征和状态。这些特征通常包括人口统计学数据、健康状况和疾病史、临床指标、实验室检测、生活方式、社会经济等。 本人在既往文章《scitb包1.6版本发…

无人机之机型区别与应用领域

一、多旋翼无人机 特点&#xff1a;多旋翼无人机依靠产生升力以平衡飞行器的重力&#xff0c;通过改变每个旋翼的转速来控制飞行姿态&#xff0c;能够悬停和垂直起降。他们具备体积小、重量轻、噪音小、隐蔽性好的特点&#xff0c;操作灵活且易于维护。 应用&#xff1a;多旋…

uni-app:文字竖直排列,并且在父级view中水平竖直对齐

一、效果 二、代码 <template><view class"parent"><text class"child">这是竖直排列的文字</text></view> </template> <script>export default {data() {return {}},methods: {},}; </script> <sty…

Ubuntu 24.04安装Jellyfin媒体服务器图解教程

使用 Jellyfin 等开源软件创建媒体服务器肯定能帮助您管理和跨各种设备传输媒体集合。当你有一个封闭社区时&#xff0c;这尤其有用。 什么是 Jellyfin 媒体服务器&#xff1f; Jellyfin 媒体服务器&#xff0c;顾名思义&#xff0c;是一款开源软件&#xff0c;允许用户使用本…

鼠标的发明和鼠标“变形记”

注&#xff1a;机翻&#xff0c;未校对。 Who Invented the Computer Mouse? 谁发明了电脑鼠标&#xff1f; It was technology visionary and inventor Douglas Engelbart (January 30, 1925 – July 2, 2013) who revolutionized the way computers worked, turning it fr…

Flink源码学习资料

Flink系列文档脑图 由于源码分析系列文档较多&#xff0c;本人绘制了Flink文档脑图。和下面的文档目录对应。各位读者可以选择自己感兴趣的模块阅读并参与讨论。 此脑图不定期更新中…… 文章目录 以下是本人Flink 源码分析系列文档目录&#xff0c;欢迎大家查阅和参与讨论。…

用 WireShark 抓住 TCP

Wireshark 是帮助我们分析网络请求的利器&#xff0c;建议每个同学都装一个。我们先用 Wireshark 抓取一个完整的连接建立、发送数据、断开连接的过程。 简单的介绍一下操作流程。 1、首先打开 Wireshark&#xff0c;在欢迎界面会列出当前机器上的所有网口、虚机网口等可以抓取…

怎样减少视频的容量 怎样减少视频内存保持清晰度

在数字媒体时代&#xff0c;视频内容已经成为人们日常交流和信息传递的重要方式。然而&#xff0c;视频往往占用大量存储空间&#xff0c;给我们的设备带来不小的负担。如何在不损失视频质量的前提下&#xff0c;减少视频文件的大小呢&#xff1f;本文将为你揭秘几个实用的技巧…

Qt 使用Installer Framework制作安装包

Qt 使用Installer Framework制作安装包 引言一、下载安装 Qt Installer Framework二、简单使用2.1 创建目录结构 (文件夹结构)2.2 制作程序压缩包2.3 制作程序安装包 引言 Qt Installer Framework (安装程序框架)是一个强大的工具集&#xff0c;用于创建自定义的在线和离线安装…

【开发指南】HTML和JS编写多用户VR应用程序的框架

1.概述 Networked-Aframe 的工作原理是将实体及其组件同步到连接的用户。要连接到房间&#xff0c;您需要将networked-scene组件添加到a-scene元素。对于要同步的实体&#xff0c;请向其添加networked组件。默认情况下&#xff0c;position和rotation组件是同步的&#xff0c;…

未来的社交标杆:如何通过AI让Facebook更加智能化?

在当今信息爆炸的时代&#xff0c;社交媒体平台的智能化已成为提高用户体验和互动质量的关键因素。Facebook&#xff0c;作为全球最大的社交平台之一&#xff0c;通过人工智能&#xff08;AI&#xff09;的广泛应用&#xff0c;正不断推进其智能化进程。本文将探讨Facebook如何…

昇思25天学习打卡营第16天 | Vision Transformer图像分类

昇思25天学习打卡营第16天 | Vision Transformer图像分类 文章目录 昇思25天学习打卡营第16天 | Vision Transformer图像分类Vision Transform&#xff08;ViT&#xff09;模型TransformerAttention模块Encoder模块 ViT模型输入 模型构建Multi-Head Attention模块Encoder模块Pa…

工业三防平板助力工厂生产数据实时管理

在当今高度数字化和智能化的工业生产环境中&#xff0c;工业三防平板正逐渐成为工厂实现生产数据实时管理的得力助手。这种创新的技术设备不仅能够在恶劣的工业环境中稳定运行&#xff0c;还为工厂的生产流程优化、效率提升和质量控制带来了前所未有的机遇。 工业生产场景通常充…

机器学习——数据预处理和特征工程(sklearn)

目录 一、数据挖掘流程 1. 获取数据 2. 数据预处理 3. 特征工程 4. 建模&#xff0c;测试模型并预测出结果 5. 验证模型效果 二、sklearn中的相关包 1.sklearn.preprocessing 2.sklearn.Impute 3.sklearn.feature_selection 4.sklearn.decomposition 三、数据预处理…

【网络安全】PostMessage:分析JS实现XSS

未经许可&#xff0c;不得转载。 文章目录 前言示例正文 前言 PostMessage是一个用于在网页间安全地发送消息的浏览器 API。它允许不同的窗口&#xff08;例如&#xff0c;来自同一域名下的不同页面或者不同域名下的跨域页面&#xff09;进行通信&#xff0c;而无需通过服务器…

【Arduino IDE】安装及开发环境、ESP32库

一、Arduino IDE下载 二、Arduino IDE安装 三、ESP32库 四、Arduino-ESP32库配置 五、新建ESP32-S3N15R8工程文件 乐鑫官网 Arduino官方下载地址 Arduino官方社区 Arduino中文社区 一、Arduino IDE下载 ESP-IDF、MicroPython和Arduino是三种不同的开发框架&#xff0c;各自适…

定制开发AI智能名片商城微信小程序在私域流量池构建中的应用与策略

摘要 在数字经济蓬勃发展的今天&#xff0c;私域流量已成为企业竞争的新战场。定制开发AI智能名片商城微信小程序&#xff0c;作为私域流量池构建的创新工具&#xff0c;正以其独特的优势助力企业实现用户资源的深度挖掘与高效转化。本文深入探讨了定制开发AI智能名片商城微信…

AIoTedge智能物联网边缘计算平台:引领未来智能边缘技术

引言 随着物联网技术的飞速发展&#xff0c;我们正步入一个万物互联的时代。AIoTedge智能物联网边缘计算平台&#xff0c;以其创新的边云协同架构&#xff0c;为智能设备和系统提供了强大的数据处理和智能决策能力&#xff0c;开启了智能物联网的新篇章。 智能边缘计算平台的核…

LLaMA-Factory

文章目录 一、关于 LLaMA-Factory项目特色性能指标 二、如何使用1、安装 LLaMA Factory2、数据准备3、快速开始4、LLaMA Board 可视化微调5、构建 DockerCUDA 用户&#xff1a;昇腾 NPU 用户&#xff1a;不使用 Docker Compose 构建CUDA 用户&#xff1a;昇腾 NPU 用户&#xf…