【Linux】信号量和线程池

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:折纸花满衣
🏠个人专栏:题目解析
🌎推荐文章:【Linux】进程通信——共享内存+消息队列+信号量

在这里插入图片描述


目录

  • 👉🏻信号量
  • 👉🏻POSIX信号量函数
    • sem_init
    • sem_wait
    • sem_post and sem_destroy
  • 👉🏻基于环形队列的生产者消费者模型使用信号量实现线程同步
    • 环形队列
    • RingQueue.hpp
    • LockGuard.hpp
  • 👉🏻线程池
    • 小故事理解线程池

👉🏻信号量

在【Linux】进程通信——共享内存+消息队列+信号量中我们已经初步了解过了信号量。
信号量,一种用于进程间通信和同步的机制,主要用来控制对共享资源的访问。通过信号量,可以确保多个进程之间能够有序地访问共享资源,避免数据竞争等问题。

现在,让我们来举一个幽默风趣的例子来帮助理解信号量的概念:

假设有一个办公室里只有一个咖啡机,而办公室里有三个员工:小明、小红和小李。每个员工都爱喝咖啡,但是咖啡机一次只能供应一个人使用。

这时,我们可以用一个信号量来模拟这个场景。信号量的初始值为1,代表咖啡机可供使用。当一个员工想要喝咖啡时,他会尝试获取信号量,如果信号量的值大于0(咖啡机可供使用),他就可以使用咖啡机,然后将信号量减1。当他喝完咖啡后,会释放信号量,让其他员工可以使用咖啡机。

如果此时另外两个员工也想要喝咖啡,由于信号量的值已经为0,他们会等待直到有人释放信号量为止。这样就避免了多个员工同时使用咖啡机,保证了咖啡机的有序使用。

信号量本质就是一个资源的计数器

👉🏻POSIX信号量函数

在【Linux】进程通信——共享内存+消息队列+信号量中我们已经学过了SystemV信号量函数的使用,而在这篇文章里,我们将学习POSIX信号量函数进行实现线程间的同步。

POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。 但POSIX可以用于线程间同步

sem_init

函数原型:

int sem_init(sem_t *sem, int pshared, unsigned int value);

参数意义:

  • sem:指向要初始化的信号量的指针。
  • pshared:指定信号量的类型。如果为0,表示信号量是进程内共享的;如果非0,表示信号量可以在进程间共享(需要使用命名信号量)。
  • value:指定信号量的初始值。

函数功能:该函数用于初始化一个信号量。

使用代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>int main() {sem_t mySemaphore;// 初始化一个进程内共享的信号量,初始值为1if (sem_init(&mySemaphore, 0, 1) == -1) {perror("Semaphore initialization failed");exit(EXIT_FAILURE);}// 在这里可以使用信号量进行同步操作// ...// 销毁信号量sem_destroy(&mySemaphore);return 0;
}

在这个示例中,我们使用sem_init函数初始化了一个进程内共享的信号量mySemaphore,初始值为1。接下来可以在代码中使用这个信号量进行同步操作。最后,在程序结束前使用sem_destroy函数销毁信号量。

sem_wait

函数原型:

int sem_wait(sem_t *sem);

参数意义:

  • sem:指向要操作的信号量的指针。

函数功能:该函数用于对信号量进行等待操作。如果信号量的值大于0,表示可以继续执行;如果信号量的值为0,则调用该函数的线程会被阻塞,直到信号量的值变为非零。

使用代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>int main() {sem_t mySemaphore;// 初始化一个进程内共享的信号量,初始值为1if (sem_init(&mySemaphore, 0, 1) == -1) {perror("Semaphore initialization failed");exit(EXIT_FAILURE);}// 等待信号量的值变为非零if (sem_wait(&mySemaphore) == -1) {perror("Semaphore wait failed");exit(EXIT_FAILURE);}// 在这里可以执行需要同步的操作// ...// 释放信号量if (sem_post(&mySemaphore) == -1) {perror("Semaphore post failed");exit(EXIT_FAILURE);}// 销毁信号量sem_destroy(&mySemaphore);return 0;
}

在这个示例中,我们使用sem_init函数初始化了一个进程内共享的信号量mySemaphore,初始值为1。接下来,调用sem_wait函数等待信号量的值变为非零。如果信号量的值为0,调用线程会被阻塞,直到信号量的值变为非零。在需要同步的操作完成后,我们使用sem_post函数释放信号量。最后,在程序结束前使用sem_destroy函数销毁信号量。

sem_post and sem_destroy

sem_post 函数介绍:
函数原型:

int sem_post(sem_t *sem);

参数意义:

  • sem:指向要操作的信号量的指针。

函数功能:该函数用于对信号量进行释放操作。它会将信号量的值加1,并唤醒等待该信号量的线程(如果有的话)。

发布信号量,表示资源使用完毕,可以归还资源了


sem_destroy 函数介绍:

函数原型:

int sem_destroy(sem_t *sem);

参数意义:

  • sem:指向要销毁的信号量的指针。

函数功能:该函数用于销毁一个信号量,并释放其占用的资源。调用 sem_destroy 后,该信号量就不能再被使用,需要重新初始化才能再次使用。

👉🏻基于环形队列的生产者消费者模型使用信号量实现线程同步

环形队列

在这里插入图片描述
这里我们的规则是:
1.生产者不能把消费者套一个圈
2.消费者也不能超过生产者

只有为空和为满两种情况二者会指向同一位置,其它情况都是异步操作。
为空时只能让生产者跑,为满时只能让消费者跑。

RingQueue.hpp

#pragma once#include <iostream>
#include <vector>
#include <semaphore.h>
#include "LockGuard.hpp"const int defaultsize = 5;template <class T>
class RingQueue
{
private:void P(sem_t &sem){sem_wait(&sem);}void V(sem_t &sem){sem_post(&sem);}public:RingQueue(int size = defaultsize): _ringqueue(size), _size(size), _p_step(0), _c_step(0){sem_init(&_space_sem, 0, size);sem_init(&_data_sem, 0, 0);pthread_mutex_init(&_p_mutex, nullptr);pthread_mutex_init(&_c_mutex, nullptr);}void Push(const T &in){// 生产// 先加锁1,还是先申请信号量?2P(_space_sem);//这里先申请信号量再加锁,,提高了效率。就比如看电影,大家先都把票买好了,等放映的那天就不用一个接着一个再买票。{LockGuard lockGuard(&_p_mutex);_ringqueue[_p_step] = in;_p_step++;_p_step %= _size;}V(_data_sem);//}void Pop(T *out){// 消费P(_data_sem);{LockGuard lockGuard(&_c_mutex);*out = _ringqueue[_c_step];_c_step++;_c_step %= _size;}V(_space_sem);}~RingQueue(){sem_destroy(&_space_sem);sem_destroy(&_data_sem);pthread_mutex_destroy(&_p_mutex);pthread_mutex_destroy(&_c_mutex);}private:std::vector<T> _ringqueue;int _size;int _p_step; // 生产者的生产位置int _c_step; // 消费位置sem_t _space_sem; // 生产者的信号量(计数器)sem_t _data_sem;  // 消费者的信号量(计数器)pthread_mutex_t _p_mutex;pthread_mutex_t _c_mutex;
};
  • P 操作(等待信号量):如果信号量的计数器大于零,则将计数器减一,进程继续执行;否则,进程进入等待状态。
  • V 操作(释放信号量):将信号量的计数器加一,唤醒等待该信号量的其他进程。

总而言之在这里,P操作就是进行资源1——>资源2的利用转换(上述代码是空间和数据的转换)并对旧资源信号量的计数器减1,V操作就是对新增资源信号量的计数器加1

LockGuard.hpp

#pragma once#include <pthread.h>// 不定义锁,默认认为外部会给我们传入锁对象
class Mutex
{
public:Mutex(pthread_mutex_t *lock):_lock(lock){}void Lock(){pthread_mutex_lock(_lock);}void Unlock(){pthread_mutex_unlock(_lock);}~Mutex(){}private:pthread_mutex_t *_lock;
};class LockGuard
{
public:LockGuard(pthread_mutex_t *lock): _mutex(lock){_mutex.Lock();}~LockGuard(){_mutex.Unlock();}
private:Mutex _mutex;
};

👉🏻线程池

在这里插入图片描述

线程池(Thread Pool)是一种多线程处理任务的机制,它包含一个线程集合,这些线程在后台等待任务,并在任务到来时执行任务。线程池通常用于提高多线程应用程序的性能和效率,避免频繁创建和销毁线程所带来的开销。

线程池的工作原理和优势:

  1. 工作原理:

    • 线程池由一组预先创建好的线程组成,这些线程在初始化时被启动并保持运行状态。
    • 当有任务到达时,线程池会从空闲线程中选择一个线程来执行任务,而不是每次都创建新线程。
    • 执行完任务后,线程不会销毁,而是继续保持在线程池中,可以继续执行其他任务。
  2. 优势:

    • 降低线程创建和销毁的开销:线程池中的线程可以重复利用,减少了线程创建和销毁的开销。
    • 控制线程数量:线程池可以限制同时运行的线程数量,避免线程过多导致系统资源耗尽。
    • 提高响应速度:线程池中的线程在任务到达时立即执行,不需要等待线程创建,提高了任务的响应速度。
    • 资源管理:通过线程池可以更好地管理资源,控制并发度,避免系统负载过重。

常见线程池的组成部分:

  1. 工作队列(Task Queue): 用于存放待执行的任务,线程池中的空闲线程会从队列中取出任务执行。

  2. 线程管理模块: 负责线程的创建、销毁和分配任务给线程执行。

  3. 线程池管理模块: 负责线程池的初始化、销毁,以及控制线程数量等参数的设置。

  4. 线程: 线程池中实际执行任务的工作单元。

总结:
线程池是一种用于管理多线程任务执行的机制,通过预先创建一组线程并维护一个任务队列,可以有效提高多线程应用程序的性能和效率,减少资源消耗和提高系统响应速度。在实际应用中,线程池被广泛应用于各种需要并发处理任务的场景,如网络服务器、数据库连接池等。

小故事理解线程池

假设你是一家餐厅的老板,经常会有顾客来吃饭。为了提高效率,你决定雇佣一些服务员来为顾客提供服务。
在这里插入图片描述

线程池的比喻:

你创建了一个名为"服务员线程池"的团队,该团队由多个服务员组成。他们在餐厅的大厅里等待顾客的到来。

  1. 工作队列(Task Queue): 这个队列就像是餐厅门口的候位区,顾客到来后会排队等待就餐。每个顾客就是一个任务,需要被执行。

  2. 线程管理模块: 你作为老板负责管理这些服务员。当有顾客(任务)到来时,你从候位区(工作队列)中选择一个空闲的服务员(线程)来接待顾客,并将任务分配给他。

  3. 线程池管理模块: 你根据餐厅的需求决定雇佣多少个服务员(线程),并设置最大的服务员数量。如果餐厅太忙,所有的服务员都在忙碌,新到来的顾客需要等待。但是如果餐厅没有太多的顾客,有些服务员就会闲置在那里。

  4. 线程: 每个服务员都是一个线程,他们在餐厅中接待顾客(执行任务)。一旦任务完成,服务员(线程)不会被销毁,而是回到等待区(线程池),继续等待下一个顾客(任务)的到来。


如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

微信小程序原生<map>地图实现标记多个位置以及map 组件 callout 自定义气泡

老规矩先上效果图: 1 、在pages文件夹下新建image文件夹用来存放标记的图片。 2、代码片段 也可以参考小程序文档:https://developers.weixin.qq.com/miniprogram/dev/component/map.html index.wxml代码 <mapid="map"style="width: 100%; height:100%;&…

mybatis源码阅读系列(二)

前言 上一篇文章mybatis源码阅读系列&#xff08;一&#xff09;介绍了mybatis和原生jdbc的区别&#xff0c;并通过代码展示了两者的运行过程和结果&#xff0c;下面让我们继续详细了解下mybatis的执行过程&#xff1b; package com.wyl.mybatis.service;import com.wyl.mybat…

openCV实现拖拽虚拟方块

一、项目效果&#xff1a; 二、核心流程&#xff1a; openCV读取视频流、在每一帧图片上画一个矩形。使用mediapipe获取手指关键点坐标。根据手指坐标位置和矩形的坐标位置&#xff0c;判断手指点是否在矩形上&#xff0c;如果在则矩形跟随手指移动。 三、代码流程&#xff1…

【系统架构师】-第3章-信息系统基础知识

1、信息系统的基本功能 输入&#xff1a;决定于系统所要达到的目的及系统的能力和信息环境的许可存储&#xff1a;存储各种信息资料和数据的能力处理&#xff1a;数据处理工具&#xff0c;利用OLAP、DM&#xff08;数据挖掘&#xff09;技术输出&#xff1a;保证最终实现最佳的…

AI人员入侵识别摄像机

AI人员入侵识别摄像机是一种智能监控设备&#xff0c;利用人工智能技术辨认并报警可能的入侵行为。这种摄像机利用深度学习算法实时分析监控画面&#xff0c;识别出普通行人和潜在入侵者之间的差异&#xff0c;从而更准确地预警可能发生的安全事件。 该摄像机通过对比数据库中存…

springboot项目学习-瑞吉外卖(1)

第一天任务如下&#xff1a; 建立基本架构完成登录、退出功能 注意&#xff1a;本博客没有使用网上教程里的mybatis-plus&#xff0c;使用的是mybatis&#xff1b;数据库连接池也没有使用教程里的druid&#xff0c;使用的是spring自带的连接池 基本架构 common包&#xff1a;存…

【Linux进程状态】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 一、直接谈论Linux的进程状态 看看Linux内核源代码怎么说 1.1、R状态 -----> 进程运行的状态 1.2、S状态 -----> 休眠状态(进程在等待“资源”就绪) 1.3、T状…

奇怪的比赛(Python,递归,状态压缩动态规划dp)

目录 前言&#xff1a;题目&#xff1a;思路&#xff1a;递归&#xff1a;代码及详细注释&#xff1a; 状态压缩dp&#xff1a;代码及详细注释&#xff1a; 总结&#xff1a; 前言&#xff1a; 这道题原本是蓝桥上的题&#xff0c;现在搜不到了&#xff0c;网上关于此题的讲解…

Python爬虫:原理与实战

引言 在当今的信息时代&#xff0c;互联网上的数据如同浩瀚的海洋&#xff0c;充满了无尽的宝藏。Python爬虫作为一种高效的数据抓取工具&#xff0c;能够帮助我们轻松地获取这些数据&#xff0c;并进行后续的分析和处理。本文将深入探讨Python爬虫的原理&#xff0c;并结合实战…

kkview远程控制: 内网远程桌面控制软件

内网远程桌面控制软件&#xff1a;高效、安全的远程管理方案 在信息技术日新月异的今天&#xff0c;内网远程桌面控制软件已成为许多企业和个人用户不可或缺的工具。这类软件允许用户通过内部网络&#xff0c;实现对其他计算机的远程访问和控制&#xff0c;从而大大提高工作效…

[Windows] Win11 常用快捷键

文章目录 &#x1f680; [Windows] Win11 常用快捷键&#x1f310; Windows 操作系统&#x1f525; Windows 11 &#x1f310; Windows 11 快捷键概览&#x1f525; 基本快捷键&#x1f525; 窗口快捷键&#x1f525; 功能快捷键 &#x1f4dd; 小结 &#x1f680; [Windows] W…

“技多不压身”是什么意思?看完这篇文章你会明白:有了手艺,走遍天下都不怕!

“技多不压身”是什么意思&#xff1f;看完这篇文章你会明白&#xff1a;有了手艺&#xff0c;走遍天下都不怕&#xff01; 咱们的老祖宗流传一句话&#xff1a;“一招鲜&#xff0c;吃遍天。”这话说得直白&#xff0c;却道出了学一门手艺或技术对于人生的重要性。“李秘书讲…

使用Loadrunner进行性能测试

一、确定性能测试的范围、要求、配置、工具等 明确测试的系统&#xff1a; 本文档主要指的是web应用。 明确测试要求&#xff1a; 用户提出性能测试&#xff0c;例如&#xff0c;网站首页页面响应时间在3S之内&#xff0c;主要的业务操作时间小于10s&#xff0c;支持300用户在…

Android Studio实现内容丰富的安卓宠物用品商店管理系统

获取源码请点击文章末尾QQ名片联系&#xff0c;源码不免费&#xff0c;尊重创作&#xff0c;尊重劳动。 项目编号128 1.开发环境android stuido jdk1.8 eclipse mysql tomcat 2.功能介绍 安卓端&#xff1a; 1.注册登录 2.系统公告 3.宠物社区&#xff08;可发布宠物帖子&#…

this是什么?为什么要改变this?怎么改变 this 指向?

目录 this 是什么&#xff1f; 箭头函数中的 this 为什么要改变 this 指向&#xff1f; 改变 this 指向的三种方法 call(无数个参数) apply(两个参数) bind(无数个参数) this 是什么&#xff1f; 在对象方法中&#xff0c;this 指的是所有者对象&#xff08;方法的拥有者…

提升口才表达能力的重要性与途径

提升口才表达能力的重要性与途径 口才表达能力&#xff0c;即一个人通过口头语言准确、流畅、生动地传达思想、情感和观点的能力&#xff0c;是现代社会中不可或缺的一项基本技能。无论是在职场沟通、人际交往还是公共场合发言&#xff0c;优秀的口才表达能力都能为我们带来诸…

力扣hot100:416.分割等和子集(组合/动态规划/STL问题)

组合数问题 我们思考一下&#xff0c;如果要把数组分割成两个子集&#xff0c;并且两个子集的元素和相等&#xff0c;是否等价于在数组中寻找若干个数使之和等于所有数的一半&#xff1f;是的&#xff01; 因此我们可以想到&#xff0c;两种方式&#xff1a; ①回溯的方式找到t…

vanna:基于RAG的text2sql框架

文章目录 vanna简介及使用vanna的原理vanna的源码理解总结参考资料 vanna简介及使用 vanna是一个开源的利用了RAG的SQL生成python框架&#xff0c;在2024年3月已经有了5.8k的star数。 Vanna is an MIT-licensed open-source Python RAG (Retrieval-Augmented Generation) fram…

探讨大世界游戏的制作流程及技术——大场景制作技术概况篇

接上文&#xff0c;我们接下来了解一下大世界场景制作技术有哪些&#xff0c;本篇旨在给大家过一遍目前业界的做法&#xff0c;能让大家有一个宏观的知识蓝图。实际上&#xff0c;针对不同的游戏类型和美术风格&#xff0c;制作技术在细节上有着非常大的不同&#xff0c;业界目…

HarmonyOS NEXT应用开发—自定义视图实现Tab效果

介绍 本示例介绍使用Text、List等组件&#xff0c;添加点击事件onclick,动画&#xff0c;animationTo实现自定义Tab效果。 效果预览图 使用说明 点击页签进行切换&#xff0c;选中态页签字体放大加粗&#xff0c;颜色由灰变黑&#xff0c;起到强调作用&#xff0c;同时&…