Linux初阶——线程(Part3):POSIX 信号量 CP 模型变体

一、什么是 POSIX 信号量

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

1、PV 操作

pv操作就是一种让信号量变化的操作。其中 P 操作可以让信号量减 1(如果信号量大于 0),V 操作可以让信号量加 1.

2、信号量类型——sem_t

3、相关函数

3.1. 初始化信号量

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

参数:

pshared: 0表示线程间共享,非零表示进程间共享

value:信号量初始值

3.2. 销毁信号量

int sem_destroy(sem_t *sem);

3.3. P 操作

int sem_wait(sem_t *sem);

3.4. V 操作

int sem_post(sem_t *sem);

二、环形生产者消费者模型

1、原理

对于这个模型有一个规则:

  1. 只能有一个消费者和一个生产者访问这个环形内存;不能多个消费者和多个生产者同时访问这个环形内存。
  2. 消费者不能超过生产者,也不能套生产者的圈。
  3. 生产者不能套消费者的圈。 

 2、代码

#pragma once

#include <vector>
#include <semaphore.h>
#include <pthread.h>

const int default_cap = 5;

template<class T>
class ring_queue
{
private:
    void P(sem_t& sem) { sem_wait(&sem); }
    void V(sem_t& sem) { sem_post(&sem); }
    void Lock(pthread_mutex_t& mutex) { pthread_mutex_lock(&mutex); }
    void UnLock(pthread_mutex_t& mutex) { pthread_mutex_unlock(&mutex); }
public:
    ring_queue(int capacity = default_cap) : _capacity(capacity), _arr(std::vector<T>(capacity))
    {
        sem_init(&_sem_consumer, 0, 0);
        sem_init(&_sem_producer, 0, _capacity);
        pthread_mutex_init(&_mutex_consumer, nullptr);
        pthread_mutex_init(&_mutex_producer, nullptr);
    }

    ~ring_queue()
    {
        sem_destroy(&_sem_consumer), sem_destroy(&_sem_producer);
        pthread_mutex_destroy(&_mutex_consumer), pthread_mutex_destroy(&_mutex_producer);
    }

    void push(const T& in)
    {
        P(_sem_producer); // pv 操作一步到位,不会被中断,因此可以保证结果正确
        Lock(_mutex_producer); // _index_producer 下标互斥

        _arr[_index_producer] = in;
        _index_producer = (_index_producer + 1) % _capacity;

        UnLock(_mutex_producer);
        V(_sem_consumer);
    }

    T pop()
    {
        P(_sem_consumer); // pv 操作一步到位,不会被中断,因此可以保证结果正确
        Lock(_mutex_consumer); // _index_consumer 下标互斥

        T out = _arr[_index_consumer];
        _index_consumer = (_index_consumer + 1) % _capacity;

        UnLock(_mutex_consumer);
        V(_sem_producer);
        return out;
    }
private:
    std::vector<T> _arr;
    int _capacity;
    int _index_consumer, _index_producer;
    sem_t _sem_consumer, _sem_producer;
    pthread_mutex_t _mutex_consumer, _mutex_producer;
};

三、线程池 

1、结构

​​​​​​​​​​​​​​

因为中间的共享内存是被写任务的线程和拿任务的线程共享,因此我们可以把线程池看作是多生产者和多消费者的 CP 模型。

四、线程安全单例模式

1、什么是单例模式

只能建立一个对象的设计模式。

2、单例模式类型

2.1. 懒汉模式

获取实例的时候才开辟空间并初始化。

2.2. 饿汉模式

获取实例前就已经开辟空间并初始化好了。

3、代码(懒汉版)

#ifndef __THREAD_POOL_HPP__
#define __THREAD_POOL_HPP__

#include <vector>
#include <queue>
#include <pthread.h>

const int default_cap = 5;

class thread_info
{
    pthread_t _tid;
    std::string name;
};

template<class task>
class thread_pool
{
private:
    void lock() { pthread_mutex_lock(&_mutex); }
    void unlock() { pthread_mutex_unlock(&_mutex); }
    ~thread_pool() { pthread_mutex_destroy(&_mutex); pthread_cond_destroy(&_cond); }
    thread_pool(int capacity = default_cap) : _capacity(capacity), _tasks(std::vector<task>(capacity))
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }
    thread_pool(const thread_pool<task>& pool) = delete;
    thread_pool<task>& operator=(const thread_pool<task>& pool) = delete;
public:
    static thread_pool<task>* get_instance()
    {
        if (pt == nullptr) // 第一次申请单例时才要考虑多线程互斥问题,因为后面的 _pt 都不为空了,因此不用判断 _pt 是否为空
        {
            pthread_mutex_lock(&mutex);
            if (pt != nullptr) return pt;
            pthread_mutex_unlock(&mutex);
            thread_pool<task>* ret = new thread_pool<task>;
        }
        return ret;
    }
    // pthread_create 要求的函数是 void* start_routine(void* args),而如果不加 static,则为 void* start_routine(thread_pool<task>* this, void* args)
    static void* start_routine(void* args) 
    {
        thread_pool<task>* obj = static_cast<thread_pool<task>*>(args);

        obj->lock();
        while ((obj->_tasks).empty()) pthread_cond_wait(&_cond, &_mutex);
        task t = obj->pop();
        obj->unlock();

        // run t

        return t;
    }

    void start()
    {
        for (int i = 0; i < _capacity; i++)
            _threads[i].name = "thread - " + std::to_string(i),
            pthread_create(&(_threads[i]._tid), nullptr, start_routine, this);
    }

    void push(const task& in)
    {
        lock();
        _tasks.push(in);
        pthread_cond_signal(&_cond);
        unlock();
    }

    task pop() // 可保证在调 pop 方法时只有一个线程在调
    {
        task out = _tasks.front();
        _tasks.pop();
        return out;
    }
private:
    std::vector<thread_info> _threads;
    std::queue<task> _tasks;
    int _capacity;
    pthread_mutex_t _mutex; // 抢任务执行
    pthread_cond_t _cond; // 消费者的阻塞队列

    static thread_pool<task>* pt;
    static pthread_mutex_t mutex;
};

template<class task>
thread_pool<task>* thread_pool<task>::pt = nullptr;

template<class task>
pthread_mutex_t thread_pool<task>::mutex = PTHREAD_MUTEX_INITIALIZER;

#endif

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《女巫攻击:潜伏在网络背后的隐秘威胁与防御策略》

目录 引言 一、基本概念 二、攻击机制 三、Sybil攻击类型 1、直接通信 2、间接通信 3、伪造身份 4、盗用身份 5、同时攻击 6、非同时攻击 四、攻击影响 五、防御措施 总结 引言 随着区块链技术和去中心化网络的迅速发展&#xff0c;网络安全问题也愈发引起关注。其…

Mybatis-plus入门教程

注意版本 jdk 18 springboot 3.1.0 mybatis 3.0.3 mybatisplus 3.5.5 快速入门 构建模块 导入依赖 <properties><maven.compiler.source>18</maven.compiler.source><maven.compiler.target>18</maven.compiler.target><project.build…

插件式模块化软件框架的思想图解一(框架篇)

插件式模块化软件框架的思想图解一&#xff08;框架篇&#xff09; Chapter1 插件式模块化软件框架的思想图解一&#xff08;框架篇&#xff09;一、前述二、模块化原则1、高度独立2、接口规范 三、从管理需求出发四、框架雏形五、接口引用规定六、子模块与代码模板七、把优秀当…

用ChatGPT-o1搞定论文写作!完整的8步指南

学境思源&#xff0c;一键生成论文初稿&#xff1a; AcademicIdeas - 学境思源AI论文写作 使用ChatGPT辅助论文写作可以显著提升效率和质量&#xff0c;关键在于正确的方法和对学术规范的遵守。以下将详细说明完整步骤&#xff0c;并提供ChatGPT的具体操作指南。 1. 确定研究…

LabVIEW继电器视觉检测系统

随着制造业的自动化与高精度要求不断提升&#xff0c;传统的人工检测方法逐渐难以满足高效和高精度的需求。特别是在航空航天、医疗设备等高端领域&#xff0c;密封继电器推动杆部件的质量直接影响到设备的性能与可靠性。LabVIEW自动化视觉检测系统&#xff0c;能对推动杆部件进…

SYN590RH

一般描述 SYN590RH是SYNOXO全新开发设计的一款宽电压范围&#xff0c;低功耗&#xff0c;高性能&#xff0c;无需外置AGC电容&#xff0c;灵敏度达到典型-110 dBm,400MHz~450MHz频率范围应用的单芯片ASK或00 K射频接收器。 SYN590RH是一款典型的即插即用型单片高…

网络编程_day6

目录 【0】复习 并发服务器实现思路梳理 多进程 多线程 IO多路复用select 【1】setsockopt&#xff1a;设置套接字属性 socket属性 设置地址重用 【2】超时检测 必要性 超时检测的设置方法 1. 通过函数自带的参数设置 2. 通过设置套接字属性进行设置 3. alarm函数与sigaction函…

Python Matplotlib:基本图表绘制指南

Python Matplotlib&#xff1a;基本图表绘制指南 Matplotlib 是 Python 中一个非常流行的绘图库&#xff0c;它以简单易用和功能丰富而闻名&#xff0c;适合各种场景的数据可视化需求。在数据分析和数据科学领域&#xff0c;Matplotlib 是我们展示数据的有力工具。本文将详细讲…

在VS中安装chatGPT

2、在VSCode中打开插件窗口 3、输入ChatGPT 4、这里有个ChatGPT中文版&#xff0c;就它了 5、安装 6、这时候侧边栏多了一个chatGPT分页图标&#xff0c;点击它 7、打个招呼 8、好像不行 9、看一下细节描述 10、根据要求按下按下快捷键 Ctrl Shift P 11、切换成国内模式 12、…

使用 ADB 在某个特定时间点点击 Android 设备上的某个按钮

前提条件 安装 ADB&#xff1a;确保你已经在计算机上安装了 Android SDK&#xff08;或单独的 ADB&#xff09;。并将其添加到系统环境变量中&#xff0c;以便你可以在命令行中运行 adb。 USB调试&#xff1a;确保 Android 设备已启用 USB 调试模式。这可以在设备的“设置” -…

一文了解Linux内核I2C子系统,驱动苹果MFI加密芯片

版本 日期 作者 变更表述 1.0 2024/10/27 于忠军 文档创建 背景&#xff1a;由于苹果有一套MFI IAP2的蓝牙私有协议&#xff0c;这个协议是基于BR/EDR的RFCOMM自定义UUID来实现IAP2协议的通信&#xff0c;中间会牵扯到苹果加密芯片的I2C读取&#xff0c;所以我们借此机…

Windows 部署非安装版Redis

1.下载Redis https://github.com/microsoftarchive/redis/releases 选择下载zip包&#xff0c;如Redis-x64-3.0.504.zip&#xff0c;并解压 2.启动非安装版redis服务 进入到redis目录&#xff0c;打开cmd 执行命令 redis-server.exe redis.windows.conf 3.登录redis客户端…

多个玩家在线游戏

这张图片列出了多人游戏的两种主要网络架构类型&#xff1a; 1. Peer-to-Peer (P2P)&#xff1a; 点对点网络&#xff0c;其中每个玩家的游戏客户端直接与其他玩家的游戏客户端通信。这种架构通常用于小型或中型规模的多人游戏。 2. Client-Server&#xff1a; 客户端-服务器…

JavaIO流操作

目录 简介 字节输入流 获取字节输入流 读 关闭输入流 字节输出流 获取字节输出流 写 换行符 刷新 关闭输出流 字符流输入流 获取字符输入流 读 关闭输入流 字符输出流 获取字符输出流 写 换行符 刷新 关闭输出流 简介 IO流分为两大派系&#xff1a; …

并查集与LRUCache(Java数据结构)

前言&#xff1a; 学习过二叉树之后就应该知道了如何构建一颗二叉树&#xff0c;双亲结点和孩子节点的关系&#xff0c;甚至可以放在顺序表中去构建一棵二叉树&#xff01; 接下来我们要以另一种方式去组织一棵树&#xff1a; 如何表示一棵树之间的关系&#xff1f;(这棵…

Nature Communications|基于深度学习的HE染色组织向特殊染色的转换

工作速览 病理学是通过视觉检查组织切片来进行的&#xff0c;这些切片通常用组织化学染色法染色。虽然苏木精和伊红&#xff08;H&E&#xff09;染色最为常用&#xff0c;但特殊染色可以为不同的组织成分提供额外的对比度。 **在这里&#xff0c;作者展示了从H&E染色…

阿里国际2025届校园招聘 0826算法岗笔试

目录 1. 第一题2. 第二题3. 第三题 ⏰ 时间&#xff1a;2024/08/26 &#x1f504; 输入输出&#xff1a;ACM格式 ⏳ 时长&#xff1a;100min 本试卷分为单选&#xff0c;多选&#xff0c;编程三部分&#xff0c;这里只展示编程题。 1. 第一题 题目描述 小红有一个大小为 n …

goframe开发一个企业网站 模版界面5

html或者说是模板的控制 以下是是系统的设置 server:address: ":8000"serverRoot: "resource/public" #这里要加上&#xff0c;为以后的静态文件的引入准备openapiPath: "/api.json"swaggerPath: "/swagger"cookieMaxAge: "365…

适配器模式:类适配器与对象适配器

适配器模式是一种结构性设计模式&#xff0c;旨在将一个接口转换成客户端所期望的另一种接口。它通常用于解决由于接口不兼容而导致的类之间的通信问题。适配器模式主要有两种实现方式&#xff1a;类适配器和对象适配器。下面&#xff0c;我们将详细探讨这两种方式的优缺点及适…

如何在Linux系统中使用Ansible进行自动化部署

如何在Linux系统中使用Ansible进行自动化部署 Ansible简介 安装Ansible 在Debian/Ubuntu系统中安装 在CentOS/RHEL系统中安装 Ansible的基本概念 Inventory文件 Playbooks Modules 创建Inventory文件 编写第一个Playbook 创建Playbook文件 运行Playbook 使用Handlers 编写包…