【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://xiahunao.cn/news/2868445.html

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

相关文章

Grass推出Layer 2 Data Rollup

Grass推出Layer 2 Data Rollup Grass邀请链接最新资讯 Grass邀请链接 欢迎使用我的邀请码进行注册: 邀请链接 如果你还不知道注册流程&#xff1a;详见Grass: 出售闲置带宽实现被动收入 最新资讯 简讯&#xff1a;2024年3月13日&#xff0c;Grass宣布正在建立基于Solana的La…

linux网络服务学习(1):nfs

1.什么是nfs NFS&#xff1a;网络文件系统。 *让客户端通过网络访问服务器磁盘中的数据&#xff0c;是一种在linux系统间磁盘文件共享的方法。 *nfs客户端可以把远端nfs服务器的目录挂载到本地。 *nfs服务器一般用来共享视频、图片等静态数据。一般是作为被读取的对象&…

【ArcGISPro】道路数据下载并使用

下载 下载链接: Geofabrik 下载服务器 这些数据通常 每天更新。 下载结果 arcmap用户下载工具 10.2:http://www.arcgis.com/home/item.html?id=16970017f81349548d0a9eead0ebba39 10.3:

猜一猜“爵”在古代是哪种器具?2024年3月17日蚂蚁庄园今日答案

蚂蚁庄园是一款爱心公益游戏&#xff0c;用户可以通过喂养小鸡&#xff0c;产生鸡蛋&#xff0c;并通过捐赠鸡蛋参与公益项目。用户每日完成答题就可以领取鸡饲料&#xff0c;使用鸡饲料喂鸡之后&#xff0c;会可以获得鸡蛋&#xff0c;可以通过鸡蛋来进行爱心捐赠。其中&#…

2.3 HTML5新增的常用标签

2.3.1 HTML5新增文档结构标签 在HTML5版本之前通常直接使用<div>标签进行网页整体布局&#xff0c;常见布局包括页眉、页脚、导航菜单和正文部分。为了区分文档结构中不同的<div>内容&#xff0c;一般会为其配上不同的id名称。例如&#xff1a; <div id"h…

5个AI智能写作助手,为创作提供便利与支持

在当今信息爆炸的时代&#xff0c;写作已经成为各行各业中不可或缺的一部分。然而&#xff0c;对于许多人来说&#xff0c;写作仍然是一项具有挑战性的任务。幸运的是&#xff0c;随着人工智能技术的发展&#xff0c;AI智能写作助手的出现为我们提供了极大的便利和支持。在本文…

华为WLAN配置攻击检测功能示例

华为WLAN配置攻击检测功能示例 组网图形 图1 配置攻击检测功能组网图 配置流程组网需求配置思路配置注意事项操作步骤配置文件 配置流程 WLAN不同的特性和功能需要在不同类型的模板下进行配置和维护&#xff0c;这些模板统称为WLAN模板&#xff0c;如域管理模板、射频模板、…

如何使用ROS和easymqos快速搭建一辆语音控制导航的机器人

之前做的机器人小车基本都属于电脑或手机控制操作。目前&#xff0c;使用语音控制机器人小车运动&#xff0c;让机器人导航去指定地点&#xff0c;已经成为热门&#xff0c;并且语音识别技术已经有落地方案&#xff0c;可满足生活中的基本需要。有些语音芯片通过高算力处理器运…

llamma笔记:部署Llama2

1 申请Llama2 许可 Download Llama (meta.com) 地址似乎不能填中国 1.1 获取url 提交申请后&#xff0c;填的那个邮箱会受到一封meta发来的邮件&#xff0c;打码部分的url&#xff0c;之后会用得上 2 ubuntu/linux 端部署Llama2 2.1 git clone Llama2的github 仓库 bash g…

NFTScan 正式上线 Blast NFTScan 浏览器和 NFT API 数据服务

2024 年 3 月 15 号&#xff0c;NFTScan 团队正式对外发布了 Blast NFTScan 浏览器&#xff0c;将为 Blast 生态的 NFT 开发者和用户提供简洁高效的 NFT 数据搜索查询服务。NFTScan 作为全球领先的 NFT 数据基础设施服务商&#xff0c;Blast 是继 Bitcoin、Ethereum、BNBChain、…

智慧能源管理系统在大学校园的应用-安科瑞 蒋静

1 背景 为贯彻落实《中共中央国务院关于完整准确全面贯彻新发展理念做好碳达峰碳中和工作的意见》和《国务院关于印发2030年前碳达峰行动方案的通知》要求&#xff0c;把绿色低碳发展纳入国民教育体系。 2 传统模式的痛点 传统项目模式下的系统方案缺乏整体的能源监测和管控…

【Linux】Shell编程【二】

目录 Shell流程控制条件测试注意事项示例[ condition ]与[[ condition ]]的区别 if条件单分支语法示例1&#xff1a;统计根分区使用率示例2&#xff1a;创建目录 双分支if条件语句语法案例1&#xff1a;备份mysql数据库案例2&#xff1a;判断apache是否启动&#xff0c;如果没有…

TinTin Web3 动态精选:以太坊坎昆升级利好 Layer2,比特币减半进入倒计时

TinTin 快讯由 TinTinLand 开发者技术社区打造&#xff0c;旨在为开发者提供最新的 Web3 新闻、市场时讯和技术更新。TinTin 快讯将以周为单位&#xff0c; 汇集当周内的行业热点并以快讯的形式排列成文。掌握一手的技术资讯和市场动态&#xff0c;将有助于 TinTinLand 社区的开…

掘根宝典之C++类型别名,关键字typedef,auto,decltype

类型别名 在C中&#xff0c;我们可以使用typedef关键字或using关键字来创建类型别名。下面是两种方式的示例&#xff1a; 使用typedef关键字创建类型别名&#xff1a; typedef int myInt; typedef float myFloat;myInt a;//等价int a; myFloat b;//等价float b; 使用using关…

《Python深度学习》阅读笔记

以下是《Python深度学习》一书中学习过程中记录的一些重要的专属名词和概念&#xff1a; 一、概念 深度学习&#xff08;Deep Learning&#xff09;&#xff1a;指使用多层神经网络进行机器学习的技术。神经网络&#xff08;Neural Network&#xff09;&#xff1a;一种模仿生…

【Algorithms 4】算法(第4版)学习笔记 18 - 4.4 最短路径

文章目录 前言参考目录学习笔记0&#xff1a;引入介绍1&#xff1a;APIs1.1&#xff1a;API&#xff1a;加权有向边1.2&#xff1a;Java 实现&#xff1a;加权有向边1.3&#xff1a;API&#xff1a;加权有向图1.4&#xff1a;Java 实现&#xff1a;加权有向图1.5&#xff1a;AP…

【C语言】分支语句(逻辑运算符与关系运算符)

文章目录 **逻辑运算符(&&、||、!)**逻辑运算符特点短路短路-逻辑与短路-逻辑或 **关系运算符&#xff08;relational expression&#xff09;**运算操作符的结合律、运算符 **选择结构/分支结构****if 语句****复合句的if语句(if...else..语句)****不良风格的程序** *…

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

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

DC-DC电源管理芯片MC34063A介绍

MC34063A 为一单片 DC-DC 变换集成电路&#xff0c;内含温度补偿的参考电压源&#xff08;1.25V&#xff09;、比较器、能有效限制电流及控制工作周期的振荡器&#xff0c;驱动器及大电流输出开关管等。外配少量元件&#xff0c;就能组成升压、降压及电压反转型 DC-DC 变换器。…

计算机系统基础 2 Intel 中央处理器

Intel微处理器的发展史 INTegrated ELectronics&#xff08;集成电子&#xff09;的缩写 先后推出的中央处理器&#xff1a; Intel4004、Intel8008、Intel8080/8085、8086/8088、80186、80286、i386、i486 Pentium&#xff08;奔腾&#xff09;、Pentium II、Pentium III、Pen…