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个交易场所:

一段特点结构的缓存区

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Tkinter打包成EXE安装文件

打包成 .exe可执行文件 1. 安装PyInstaller&#xff0c;命令如下&#xff1a; pip install pyinstaller2. 编写你的Tkinter应用程序&#xff1a; 创建一个Python文件&#xff0c;例如app.py&#xff0c;并写入你的Tkinter代码。 3. 在 app.py 文件所在的目录使用PyInstaller…

从零开始讲PCIe(5)——66MHZ的PCI总线与其限制

一、前言 在之前的内容中&#xff0c;我们已经基本了解了PCI总线的设计思路和传输机制&#xff0c;之前的内容我们都是基于33MHZ版本的PCI总线进行学习的&#xff0c;为了支持更到的带宽&#xff0c;PCI协议还有一种66MHZ的版本。 二、高带宽PCI&#xff08;66MHZ&#xff09;…

UML类图全解析

1.UML的基本介绍 1.1什么是UML 1.UML > 统一建模语言&#xff0c;是一种用于软件系统分析和设计的语言工具&#xff0c;它用于帮助软件开发人员进行思考和记录思路的结果。 2.UML本身是一套符号的规定&#xff0c;就像数学符号和化学符号一样&#xff0c;这样符号用于描述软…

dll动态库加载失败导致程序启动报错以及dll库加载失败的常见原因分析与总结

目录 1、问题说明 2、dll库的隐式加载与动态加载 2.1、dll库的隐式加载 2.2、dll库的显式加载 3、使用Process Explorer查看进程加载的dll库信息以及动态加载的dll库有没有加载成功 3.1、使用Process Explorer查看进程加载的dll库信息 3.2、使用Process Explorer查看动态…

交叠型双重差分法

交叠型双重差分法&#xff08;Staggered Difference-in-Differences, Staggered DiD&#xff09;是一种扩展的双重差分&#xff08;Difference-in-Differences, DiD&#xff09;方法&#xff0c;用于处理多个时间点的政策干预或处理组&#xff08;treatment group&#xff09;并…

JavaWeb的小结02

第2章-第2节 一、知识点 HttpServletRequest请求对象、HttpServletResponse响应对象、响应内容类型Content-Type、请求转发、重定向、ServletContext对象。 二、目标 深刻理解HttpServletRequest对象的作用。 深刻理解HttpServletResponse对象的作用。 掌握HttpServletRequ…

企业必备:搭建大模型应用平台实操教程

最近AI智能体很火&#xff0c;AI智能体平台化产品肯定属于大公司的。但在一些场景下&#xff0c;尤其是对业务数据要求很高的公司&#xff0c;那就只能用私有大模型。不一定完全是为了对外提供服务&#xff0c;对内改造工作流也是需要的。所以 我感觉未来大部分企业都会搞一个…

普渡PUDU MT1:AI赋能,破解大面积场景清洁新挑战

普渡AI智能扫地机器人PUDU MT1:破解大面积场景清洁难题的新利器 在仓储物流、工业车间、交通枢纽、大型商场等大面积场景中,清洁难题一直是管理者们头疼的问题。这些区域面积广阔,清洁任务繁重,传统清洁方式难以胜任。然而,普渡机器人最新推出的AI智能扫地机器人PUDU MT1…

什么是 HTTP Get + Preflight 请求

当在 Chrome 开发者工具的 Network 面板中看到 GET Preflight 的 HTTP 请求方法时&#xff0c;意味着该请求涉及跨域资源共享 (CORS)&#xff0c;并且该请求被预检了。理解这种请求的背景&#xff0c;主要在于 CORS 的工作机制和现代浏览器对安全性的管理。 下面是在 Chrome …

ConcurrentHashMap在JDK1.7和1.8的区别,详解

目录 1.了解HashMap底层插入原理 2.ConcurrentHashMap 是什么&#xff1f; HashTable的实现 3.ConcurrentHashMap 1.7和1.8的区别 4、JDK1.7 中的ConcurrentHashMap实现原理 6、JDK1.8中的ConcurrentHashMap 7.链表转红黑树条件 1.8 put方法 8.并发扩容 9.总结 首先呢…

Origin正态分布检验

在spass中用Shapiro-Wilk检验--正态分布检测 Shapiro-Wilk检验--正态分布检测_spss shapiro-wilk检验-CSDN博客

数据服务-实时同步(sersync)

1. 概述 1.之前我们通过rsync定时任务实现定时备份/同步 2. 对于NFS我们需要进行实时同步 2. Sersync原理 3. 上手指南 环境主机web0110.0.0.7(nfs客户端)nfs0110.0.0.31(rsync客户端) (nfs服务端)backup10.0.0.41(rsync服务端) 3.1 rsync服务端准备 参考: 数据服务-备份服务…

好用便宜的头戴式耳机哪款好?强推四款高分爆单耳机精品!

音质&#xff0c;是耳机的灵魂。头戴式降噪耳机&#xff0c;以其卓越的音质表现&#xff0c;为您演绎音乐的真谛。无论是细腻的情感表达&#xff0c;还是震撼的音效体验&#xff0c;它都能让您感受到音乐的魅力所在。那好用便宜的头戴式耳机哪款好&#xff1f;&#xff0c;这里…

为什么芯片有多个不同的供电电压?

一、为什么芯片有多个不同的供电电压&#xff1f; 优化性能与功耗&#xff1a;芯片的核心部分&#xff08;Core&#xff09;和输入输出部分&#xff08;IO&#xff09;可能采用不同的电压。核心电压通常较低&#xff0c;以减少功耗和发热&#xff0c;提高能效&#xff1b;而IO电…

Linux驱动开发常用调试方法汇总

引言&#xff1a;在 Linux 驱动开发中&#xff0c;调试是一个至关重要的环节。开发者需要了解多种调试方法&#xff0c;以便能够快速定位和解决问题。 1.利用printk 描述&#xff1a; printk 是 Linux 内核中的一个调试输出函数&#xff0c;类似于用户空间中的 printf。它用于…

CSID-GAN:基于生成对抗网络的定制风格室内平面设计框架论文阅读

CSID-GAN: A Customized Style Interior Floor Plan Design Framework Based on Generative Adversarial Network 摘要前言II. CSID-GAN METHODA. Overall FrameworkB. Algorithm and Loss Function III. DATASETS AND EVALUATION METRICSA. DatasetsB. Evaluation Metrics IV.…

SAP MM学习笔记 - 豆知识10 - OMSY 初期化会计期间,ABAP调用MMPV/MMRV来批量更新会计期间(TODO)

之前用MMRV&#xff0c;MMPV来一次一个月来修改会计期间。 如果是老的测试机&#xff0c;可能是10几年前的&#xff0c;一次1个月&#xff0c;更新到当前期间&#xff0c;搞个100多次&#xff0c;手都抖。 SAP MM学习笔记 - 错误 M7053 - Posting only possible in periods 2…

【web安全】——逻辑漏洞

1.逻辑漏洞 1.1. 简介 逻辑漏洞就是指攻击者利用业务/功能上的设计缺陷,获取敏感信息或破坏业务的完整性。一般出现在密码修改、越权访问、密码找回、交易支付金额等功能处。 逻辑漏洞的破坏方式并非是向程序添加破坏内容,而是利用逻辑处理不严密或代码问题或固有不足&#x…

Timeline: 时间线轮播多图

对全国2014-2023年各省市的人口&#xff0c;做出动态柱状图/时间线轮播多图&#xff0c;即每隔一定时间间隔&#xff0c;自动的切换2014、2015、....2023各省市的人口(即2014-2023年全国省市人口排名前12的情况) 1、模板 # -*- coding: gbk -*- from pyecharts import option…

智慧农业案例 (二)- 智能化灌溉系统

橙蜂智能公司致力于提供先进的人工智能和物联网解决方案&#xff0c;帮助企业优化运营并实现技术潜能。公司主要服务包括AI数字人、AI翻译、领域知识库、大模型服务等。其核心价值观为创新、客户至上、质量、合作和可持续发展。 橙蜂智农的智慧农业产品涵盖了多方面的功能&…