linux并发服务器 —— 多线程并发(六)

线程概述

同一个程序中的所有线程均会独立执行相同程序,且共享同一份全局内存区域;

进程是CPU分配资源的最小单位,线程是操作系统调度执行的最小单位;

Linux环境下,线程的本质就是进程;

ps -Lf pid:查指定进程LWP号(线程号)

线程和进程的区别

1. 进程间的信息难以共享,除只读代码段,父子进程并未共享内存;

        线程共享信息方便快速(进程、父进程、进程组、会话ID,文件描述符表,当前工作目录,文件权限掩码,虚拟地址空间(除栈、.text));但超线程ID、信号掩码、error变量、调度策略和优先级、栈、本地变量不共享;

2. fork创建进程代价较高

        创建线程比创建进程快一个数量级以上

线程操作

/*#include <pthread.h>一般情况下,main所在线程为主线程/main线程,其余都成为子线程pthread_t pthread_self(void);功能:获取当前线程IDint pthread equal(pthread_t tl,pthread_t t2);功能:比较两个线程号是否相等不同操作系统,pthread_t类型实现不一样,有可能是结构体int pthread_create(pthread t *thread, const pthread attr t *attr,void *(*start_routine) (void *), void *arg);功能:创建一个子线程(调度的基本单位)参数:thread - 传出参数:线程创建成功,子线程ID会写入该变量attr - 设置线程的属性,默认值 - NULLstart_rountine - 函数指针,子线程需要处理的逻辑代码arg - 给start_rountine使用,传参返回值:成功 - 0失败 - 错误号,与errno不同;获取错误号信息:char* strerror(int errnum);void pthread_exit(void *retval);功能:终止一个当前调用线程参数:retval - 传递一个指针,作为一个返回值,可以在pthread_join中获取返回值: 没有任何返回值int pthread_join(pthread_t thread,void **retval);功能:和一个已经终止的线程进行连接回收子线程的资源这个函数是阻塞函数,调用一次只能回收一个子线程一般在主线程中去使用参数:thread - 需要回收的子线程IDretval - 接收子线程退出的返回值返回值:成功 - 0失败 - !0int pthread_detach(pthread_t thread);功能:分离一个线程,将线程标记分离,线程终止时自动释放资源给系统1. 不能多次分离,不可预料2. 不能去连接一个已经分离的线程,会报错(join)参数:需要分离的线程ID返回值:成功 - 0失败 - errorint pthread_cancel(pthread_t thread);功能:取消线程(让线程终止),中途暂停!但并不是立马终止,而是当一个子线程执行到一个取消点,线程才会终止取消点:系统规定好的一些系统调用,可以粗略认为是用户去到内核区的切换这个位置
*/

创建线程

#include <iostream>
#include <pthread.h>
#include <cstdio>
#include <string.h>
#include <unistd.h>
using namespace std;void* callback(void *arg){cout<<"子线程...."<<*((int*)arg)<<endl;return NULL;
}int main(){pthread_t tid;int num = 10;int ret = pthread_create(&tid , NULL , callback , (void*)&num);if(ret != 0){char* str = strerror(ret);cout<<"error: "<<str<<endl;}for(int i = 0 ; i<5 ; i++){cout<<i<<endl;}sleep(1);return 0;
}

终止线程

#include <iostream>
#include <pthread.h>
#include <cstdio>
#include <string.h>
#include <unistd.h>
using namespace std;void* callback(void *arg){char buf[1024];sprintf(buf , "子线程....%ld" , pthread_self());cout<<buf<<endl;return NULL;
}int main(){// 创建子线程pthread_t tid;int ret = pthread_create(&tid , NULL , callback , NULL);if(ret != 0){char* str = strerror(ret);cout<<str<<endl;}for(int i = 0 ; i<100 ; i++){cout<<i<<endl;}cout<<"子线程...."<<tid<<endl;cout<<"主线程...."<<pthread_self()<<endl;pthread_exit(NULL);// 主线程退出不会影响正常运行的线程return 0; // 进程退出 所有子线程立刻终止
}

链接已终止的线程

#include <iostream>
#include <pthread.h>
#include <cstdio>
#include <string.h>
#include <unistd.h>
using namespace std;int val = 10;void* callback(void *arg){char buf[1024];sprintf(buf , "子线程....%ld" , pthread_self());cout<<buf<<endl;// sleep(3);// return NULL; // pthread_exit(NULL);// int val = 10; // 局部变量pthread_exit((void*)&val);
}int main(){// 创建子线程pthread_t tid;int ret = pthread_create(&tid , NULL , callback , NULL);if(ret != 0){char* str = strerror(ret);cout<<str<<endl;}for(int i = 0 ; i<5 ; i++){cout<<i<<endl;}cout<<"子线程...."<<tid<<endl;cout<<"主线程...."<<pthread_self()<<endl;int* ptr;if(pthread_join(tid , (void **)&ptr) != 0){char* str = strerror(ret);cout<<str<<endl;}cout<<"回收子线程成功: "<<*(int *)ptr<<endl;pthread_exit(NULL);// 主线程退出不会影响正常运行的线程return 0; // 进程退出 所有子线程立刻终止
}

线程分离

#include <iostream>
#include <pthread.h>
#include <cstdio>
#include <string.h>
#include <unistd.h>
using namespace std;void* callback(void* arg){cout<<"我的ID: "<<pthread_self()<<endl;return NULL;
}int main(){// 创建pthread_t tid;int ret = pthread_create(&tid , NULL , callback , NULL);if(ret != 0){char* str = strerror(ret);cout<<"error1: "<<str<<endl;}cout<<"父线程:"<<pthread_self()<<"子线程:"<<tid<<endl;//子线程分离ret = pthread_detach(tid);if(ret != 0){char* str = strerror(ret);cout<<"error2: "<<str<<endl;}//对分离子线程进行连接ret = pthread_join(tid,NULL);if(ret != 0){char* str = strerror(ret);cout<<"error3: "<<str<<endl;}pthread_exit(NULL);return 0;
}

线程取消

#include <iostream>
#include <pthread.h>
#include <cstdio>
#include <string.h>
#include <unistd.h>
using namespace std;void* callback(void* arg){cout<<"我的ID: "<<pthread_self()<<endl;for(int i = 0 ; i<5 ; i++){cout<<"子线程:"<<i<<endl;}return NULL;
}int main(){// 创建pthread_t tid;int ret = pthread_create(&tid , NULL , callback , NULL);if(ret != 0){char* str = strerror(ret);cout<<"error1: "<<str<<endl;}// 取消线程pthread_cancel(tid);for(int i = 0 ; i<10 ; i++){cout<<i<<endl;}cout<<"父线程:"<<pthread_self()<<"子线程:"<<tid<<endl;pthread_exit(NULL);return 0;
}

线程属性

/*
int pthread_attr_init(pthread_attr_t *attr);初始化线程属性变量int pthread_attr_destroy(pthread_attr_t *attr);释放线程属性资源int pthread_attr_getdetachstate(const pthread_attq_t *attr, int* detachstate);获取线程分离的状态属性int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);设置线程分离的状态属性*/#include <iostream>
#include <pthread.h>
#include <cstdio>
#include <string.h>
#include <unistd.h>
using namespace std;void* callback(void* arg){cout<<"我的ID: "<<pthread_self()<<endl;return NULL;
}int main(){// 创建线程属性变量pthread_attr_t attr;pthread_attr_init(&attr);pthread_attr_setdetachstate(&attr , PTHREAD_CREATE_DETACHED);// 获取线程栈的大小size_t size;pthread_attr_getstacksize(&attr , &size);cout<<"子线程占空间大小:"<<size<<endl;// 创建pthread_t tid;int ret = pthread_create(&tid , &attr , callback , NULL);if(ret != 0){char* str = strerror(ret);cout<<"error1: "<<str<<endl;}cout<<"父线程:"<<pthread_self()<<"子线程:"<<tid<<endl;pthread_attr_destroy(&attr);ret = pthread_join(tid,NULL);if(ret != 0){char* str = strerror(ret);cout<<"error3: "<<str<<endl;}pthread_exit(NULL);return 0;
}

线程同步

必须确保多个线程不会同时修改同一变量,或者某一线程不会读取正在由其他线程修改的变量;

临界区是指访问某一共享资源的代码片段,这段代码的执行应该为原子操作(不能分割);

互斥锁

使用互斥锁来确保仅有一个线程可以访问某项共享资源,保证原子访问;

互斥锁由两种状态:锁定/未锁定,试图对锁定的互斥锁再加锁会导致线程阻塞/报错,取决于加锁使用的方法;

线程加锁成为互斥锁的所有者,只有所有者才能解锁;

/*
互斥量的类型 pthread_mutex_t
int pthread_mutex_init(pthread_mutex_t *restrict mutexconst pthread_mutexattr_t *restrict attr);功能:初始化互斥锁参数:mutex - 需要初始化的互斥锁attr - 互斥锁相关属性 NULLrestric - C语言修饰符,被修饰的指针不能由另外的指针进行操作
int pthread_mutex_destroy(pthread_mutex_t *mutex);释放互斥量的资源
int pthread_mutex_lock(pthread_mutex_t *mutex);加锁 , 如果有线程已经加锁,只能阻塞等待
int pthread_mutex_trylock(pthread_mutex_t *mutex);尝试加锁,加锁失败不会阻塞,会直接返回
int pthread_mutex_unlock(pthread_mutex_t *mutex);释放锁
*/

死锁

多个进程在执行过程中,因争夺共享资源而造成的一种互相等待的现象;

导致死锁的三个主要原因:

1. 加锁忘记释放

2. 重复枷锁

3. 线程之间对于锁循环等待

读写锁

读写锁允许多个读出,但只允许一个写入:

1. 如果有其他线程读数据,则允许其他线程执行读操作,但不允许写操作;

2. 有其他线程写数据,则其他线程不允许读/写;

3. 写是独占的,写的优先级高;

/*读写锁的类型 pthread_rwlock_tint pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
*/
// 案例:创建8个线程,操作同一个全局变量;
//      3个线程不定时写一个全局变量,其余5个线程不定时读全局变量
#include <iostream>
#include <pthread.h>
#include <cstdio>
#include <unistd.h>
using namespace std;int num = 1;
// pthread_mutex_t mutex;
pthread_rwlock_t rwlock;void* wnum(void* arg){while(1){// pthread_mutex_lock(&mutex);pthread_rwlock_wrlock(&rwlock);num++;printf("++write , tid: %ld , num : %d\n" , pthread_self() , num);// pthread_mutex_unlock(&mutex);pthread_rwlock_unlock(&rwlock);sleep(1);}return NULL;
}void* rnum(void* arg){while(1){// pthread_mutex_lock(&mutex);pthread_rwlock_rdlock(&rwlock);printf("read , tid: %ld , num : %d\n" , pthread_self() , num);// pthread_mutex_unlock(&mutex);pthread_rwlock_unlock(&rwlock);sleep(1);}return NULL;
}int main(){// pthread_mutex_init(&mutex , NULL);pthread_rwlock_init(&rwlock , NULL);// 创建3个写线程 5个读线程pthread_t wtids[3] , rtids[5];for(int i = 0 ; i<3 ; i++){pthread_create(&wtids[i] , NULL , wnum , NULL);}for(int i = 0 ; i<5 ; i++){pthread_create(&rtids[i] , NULL , rnum , NULL);}// 设置线程分离for(int i = 0 ; i<3 ; i++){pthread_detach(wtids[i]);}for(int i = 0 ; i<5 ; i++){pthread_detach(rtids[i]);}pthread_exit(NULL);// pthread_mutex_destroy(&mutex);pthread_rwlock_destroy(&rwlock);return 0;
}

生产者消费者模式

多生产者 - 容器 - 多消费者

阻塞 - 通知机制,需要条件变量和信号量来进行实现;

条件变量 - 通过条件变量来唤醒阻塞进程

信号量 - 一定程度上表示资源的多少

/*信号量的类型 sem_tint sem_init(sem_t *sem, int pshared,unsigned int value);初始化信号量参数:sem - 信号量变量的地址pshared - 0用在线程,非0用在进程value - 信号量中的值int sem_destroy(sem_t *sem);释放资源int sem_wait(sem_t *sem);加锁 对信号量的值减1,如果值为0则阻塞int sem_trywait(sem_t *sem);尝试int sem_timedwait(sem_t *sem, const struct timespec *abs timeout);等待多少时间int sem_post(sem_t *sem);解锁 对信号量的值加1int sem_getvalue(sem_t *sem, int *sval);获取值
*/
// 生产者消费者模型 粗略版本
#include <iostream>
#include <pthread.h>
#include <cstdio>
#include <unistd.h>
#include <cstdlib>
#include <semaphore.h>
using namespace std;// 创建一个互斥锁
pthread_mutex_t mutex;
// 创建两个信号量
sem_t p , c;class Node{
public:int num;Node* next;
};Node* head = NULL;void* pro(void* arg){// 不断创建节点添加到链表while(1){sem_wait(&p);pthread_mutex_lock(&mutex);Node* newNode = new Node();newNode->next = head;head = newNode;newNode->num = rand()%100;printf("add node , num: %d , tid: %ld\n" , newNode->num , pthread_self());   pthread_mutex_unlock(&mutex);sem_post(&c);usleep(1000);}return NULL;
}void* cus(void* arg){while(1){sem_wait(&c);pthread_mutex_lock(&mutex);Node* cur = head;head = head->next;printf("del node : %d , tid : %ld\n" , cur->num , pthread_self());delete(cur);cur = NULL;pthread_mutex_unlock(&mutex);sem_post(&p);usleep(1000);}return NULL;
}int main(){pthread_mutex_init(&mutex , NULL);sem_init(&p , 0 , 5);sem_init(&c , 0 , 0);// 5个生产者,5个消费者pthread_t ptids[5] , ctids[5];for(int i = 0 ; i<5; i++){pthread_create(&ptids[i] , NULL , pro , NULL);pthread_create(&ctids[i] , NULL , cus , NULL);}for(int i = 0 ; i<5 ; i++){pthread_detach(ptids[i]);pthread_detach(ctids[i]);}while(1){sleep(10);}pthread_mutex_destroy(&mutex);pthread_exit(NULL);return 0;
}

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

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

相关文章

简单了解ARP协议

目录 一、什么是ARP协议&#xff1f; 二、为什么需要ARP协议&#xff1f; 三、ARP报文格式 四、广播域是什么&#xff1f; 五、ARP缓存表是什么&#xff1f; 六、ARP的类型 6.1 ARP代理 6.2 免费ARP 七、不同网络设备收到ARP广播报文的处理规则 八、ARP工作机制原理 …

好玩的js特效

记录一些好玩的js特效 1、鱼跳跃特效 引入jquery:https://code.jquery.com/jquery-3.7.1.min.js 源码如下&#xff1a; <!--引入jquery--> <script src"https://code.jquery.com/jquery-3.7.1.min.js"></script> <!--引入跳跃源码--> <s…

【JavaEE】_HTML

目录 1.HTML结构 2. HTML常用标签 2.1 注释标签 2.2 标题标签&#xff1a;h1~h6 2.3 段落标签&#xff1a;p 2.4 换行标签&#xff1a;br 2.5 格式化标签 2.6 图片标签&#xff1a;img 2.7 超链接标签&#xff1a;a 2.8 表格标签 2.9 列表标签 2.10 表单标签 2.10…

【网络编程】IO多路复用

IO多路复用是一种高效的I/O处理方式&#xff0c;它允许单个进程能够同时监视多个文件描述符&#xff08;sockets、文件等&#xff09;&#xff0c;并在其中任何一个文件描述符准备好进行I/O操作时进行处理。它的核心在于使用少量的线程或进程来管理多个I/O操作&#xff0c;以提…

RK3568平台开发系列讲解(音视频篇)H264 的编码结构

🚀返回专栏总目录 文章目录 一、H264 的编码结构1.1、帧类型1.2、GOP1.3、Slice沉淀、分享、成长,让自己和他人都能有所收获!😄 📢视频编码的码流结构其实就是指视频经过编码之后得到的二进制数据是怎么组织的,换句话说,就是编码后的码流我们怎么将一帧帧编码后的图像…

通过rabbitmq生成延时消息,并生成rabbitmq镜像

通过rabbitmq生成延时消息队列&#xff0c;并生成rabbitmq镜像 整体描述1. 使用场景2. 目前问题3. 前期准备 具体步骤1. 拉取镜像2. 运行镜像3. 安装插件4. 代码支持4.1 config文件4.2 消费监听4.2 消息生产 5. 功能测试 镜像操作1. 镜像制作2. 镜像导入 总结 整体描述 1. 使用…

蠕虫病毒流量分析案例

背景 某供排水集团的网络管理员对其网络的健康状况持认可态度&#xff0c;表示网络运行正常&#xff0c;没有发现异常行为。然而&#xff0c;由于网络环境变得越来越复杂&#xff0c;仅凭借传统的网络经验已经不能全面了解网络情况。因此&#xff0c;我们为供排水集团安装了Ne…

Golang复习

golang的特点 Golang 针对并发进行了优化&#xff0c;并且在规模上运行良好 自动垃圾收集明显比 Java 或 Python 更有效&#xff0c;因为它与程序同时执行 golang数据类型 基本数据类型&#xff08;值类型&#xff09; 布尔类型 数字类型 整型 根据有符号分为&#xff1a;…

[NLP]LLM---FineTune自己的Llama2模型

一 数据集准备 Let’s talk a bit about the parameters we can tune here. First, we want to load a llama-2-7b-hf model and train it on the mlabonne/guanaco-llama2-1k (1,000 samples), which will produce our fine-tuned model llama-2-7b-miniguanaco. If you’re …

华为云API对话机器人CBS的魅力—实现简单的对话操作

云服务、API、SDK&#xff0c;调试&#xff0c;查看&#xff0c;我都行 阅读短文您可以学习到&#xff1a;人工智能AI智能的问答管理、全面的对话管理、高效训练部署 1.IntelliJ IDEA 之API插件介绍 API插件支持 VS Code IDE、IntelliJ IDEA等平台、以及华为云自研 CodeArts …

每日刷题-3

目录 一、选择题 二、编程题 1、计算糖果 2、进制转换 一、选择题 1、 解析&#xff1a;在C语言中&#xff0c;以0开头的整数常量是八进制的&#xff0c;而不是十进制的。所以&#xff0c;0123的八进制表示相当于83的十进制表示&#xff0c;而123的十进制表示不变。printf函数…

ASP.NET Core IOC容器

//IOC容器支持依赖注入{ServiceCollection serviceDescriptors new ServiceCollection();serviceDescriptors.AddTransient<IMicrophone, Microphone>();serviceDescriptors.AddTransient<IPower, Power>();serviceDescriptors.AddTransient<IHeadphone, Headp…

【SQL应知应会】索引 • Oracle版:B-树索引;位图索引;函数索引;单列与复合索引;分区索引

欢迎来到爱书不爱输的程序猿的博客, 本博客致力于知识分享,与更多的人进行学习交流 本文免费学习,自发文起3天后,会收录于SQL应知应会专栏,本专栏主要用于记录对于数据库的一些学习,有基础也有进阶,有MySQL也有Oracle 索引 • MySQL版 前言一、Oracle索引1.索引概述及分类…

upload-labs1-17思路

1 直接写一个php文件测试一下&#xff0c;发现弹窗不让上传 原理很简单&#xff0c;就是把后缀名拿出来过滤一遍&#xff0c;而白名单就是弹窗的这三个 解决方法&#xff1a; 因为这是在前端防御的一个手段&#xff0c;所以直接在浏览器设置上禁用js就行了&#xff1a; 也可…

微服务-OpenFeign基本使用

一、前言 二、OpenFeign基本使用 1、OpenFeign简介 OpenFeign是一种声明式、模板化的HTTP客户端&#xff0c;它使得调用RESTful网络服务变得简单。在Spring Cloud中使用OpenFeign&#xff0c;可以做到像调用本地方法一样使用HTTP请求访问远程服务&#xff0c;开发者无需关注…

stm32 学习笔记:GPIO输出

一、GPIO简介 引脚电平 0-3.3V,部分可容忍5V&#xff0c;对输出而言最大只能输出3.3V, 只要可以用高低电平来控制的地方&#xff0c;都可以用GPIO来完成&#xff0c;如果控制的功率比较大的设备&#xff0c;只需加入驱动电路即可 GPIO 通用输入输出口&#xff0c;可配置为 8种 …

2023国赛数学建模E题思路代码 - 黄河水沙监测数据分析

# 1 赛题 E 题 黄河水沙监测数据分析 黄河是中华民族的母亲河。研究黄河水沙通量的变化规律对沿黄流域的环境治理、气候变 化和人民生活的影响&#xff0c; 以及对优化黄河流域水资源分配、协调人地关系、调水调沙、防洪减灾 等方面都具有重要的理论指导意义。 附件 1 给出了位…

【实训】“宅急送”订餐管理系统(程序设计综合能力实训)

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 前言 大一小学期&#xff0c;我迎来了人生中的第一次实训…

2023/9/8 -- C++/QT

作业 1> 自行封装一个栈的类&#xff0c;包含私有成员属性&#xff1a;栈的数组、记录栈顶的变量 成员函数完成&#xff1a;构造函数、析构函数、拷贝构造函数、入栈、出栈、清空栈、判空、判满、获取栈顶元素、求栈的大小 02stack.h: #ifndef __02STACK_H__ #define __…

无涯教程-JavaScript - IMSIN函数

描述 IMSIN函数以x yi或x yj文本格式返回复数的正弦。复数的正弦为- $$\sin(x yi) \sin(x)\cosh(y) \cos(x)\sin(y)i $$ 语法 IMSIN (inumber)争论 Argument描述Required/OptionalInumberA Complex Number for which you want the sine.Required Notes Excel中的复数仅…