线程同步机制封装类

news/2024/9/18 14:46:34/ 标签: 网络

基础知识

RAII
  • RAII全称是“Resource Acquisition is Initialization”,直译过来是“资源获取即初始化”. 是一个用于管理资源(如内存、文件句柄、网络连接等)的编程范式
  • 在构造函数中申请分配资源,在析构函数中释放资源。因为C++的语言机制保证了,当一个对象创建的时候,自动调用构造函数,当对象超出作用域的时候会自动调用析构函数。所以,在RAII的指导下,我们应该使用类来管理资源,将资源和对象的生命周期绑定
  • RAII的核心思想是将资源或者状态与对象的生命周期绑定,通过C++的语言机制,实现资源和状态的安全管理,智能指针是RAII最好的例子
#include <iostream>
#include <memory>  // 包含智能指针的头文件class Resource {
public:Resource() { std::cout << "Resource acquired.\n"; }~Resource() { std::cout << "Resource released.\n"; }
};void useResource() {std::unique_ptr<Resource> res = std::make_unique<Resource>(); // 创建并自动管理Resource对象// 这里可以使用res指向的资源// ...
} // res自动销毁,它指向的Resource对象也随之被删除int main() {useResource();// 资源已经被释放,无需担心内存泄漏std::cout << "Back in main.\n";return 0;
}
信号量

信号量是一种特殊的变量,它只能取自然数值并且只支持两种操作:等待(P)和信号(V).假设有信号量SV,对其的P、V操作如下:

  • P,如果SV的值大于0,则将其减一;若SV的值为0,则挂起执行
  • V,如果有其他进行因为等待SV而挂起,则唤醒;若没有,则将SV值加一

信号量的取值可以是任何自然数,最常用的,最简单的信号量是二进制信号量,只有0和1两个值.

  • sem_init函数用于初始化一个未命名的信号量
  • sem_destory函数用于销毁信号量
  • sem_wait函数将以原子操作方式将信号量减一,信号量为0时,sem_wait阻塞
  • sem_post函数以原子操作方式将信号量加一,信号量大于0时,唤醒调用sem_post的线程

以上,成功返回0,失败返回errno

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>sem_t sem;  // 信号量void* producer(void* arg) {// 生产数据printf("Producing data...\n");sleep(2);  // 模拟数据生产需要的时间printf("Data ready.\n");// 发送信号sem_post(&sem);return NULL;
}void* consumer(void* arg) {// 等待信号量sem_wait(&sem);// 处理数据printf("Consuming data...\n");return NULL;
}int main() {pthread_t tid1, tid2;// 初始化信号量,初始值为0sem_init(&sem, 0, 0);// 创建生产者和消费者线程pthread_create(&tid1, NULL, producer, NULL);pthread_create(&tid2, NULL, consumer, NULL);// 等待线程结束pthread_join(tid1, NULL);pthread_join(tid2, NULL);// 销毁信号量sem_destroy(&sem);return 0;
}
互斥量(锁)

互斥锁,也成互斥量,可以保护关键代码段,以确保独占式访问.当进入关键代码段,获得互斥锁将其加锁;离开关键代码段,唤醒等待该互斥锁的线程.

  • pthread_mutex_init函数用于初始化互斥锁
  • pthread_mutex_destory函数用于销毁互斥锁
  • pthread_mutex_lock函数以原子操作方式给互斥锁加锁
  • pthread_mutex_unlock函数以原子操作方式给互斥锁解锁

以上,成功返回0,失败返回errno

#include <iostream>
#include <pthread.h>
#include <semaphore.h>pthread_mutex_t mutex;
sem_t sem;void* setData(void* arg) {// 加锁pthread_mutex_lock(&mutex);std::cout << "Setting data...\n";sleep(1);  // 模拟数据设置需要的时间std::cout << "Data set.\n";// 解锁pthread_mutex_unlock(&mutex);// 释放信号量,通知数据已准备好sem_post(&sem);return nullptr;
}void* processData(void* arg) {// 等待信号量,确保数据已经设置sem_wait(&sem);// 加锁pthread_mutex_lock(&mutex);std::cout << "Processing data...\n";sleep(1);  // 模拟数据处理需要的时间std::cout << "Data processed.\n";// 解锁pthread_mutex_unlock(&mutex);return nullptr;
}int main() {pthread_t t1, t2;// 初始化互斥锁和信号量pthread_mutex_init(&mutex, nullptr);sem_init(&sem, 0, 0);// 创建线程pthread_create(&t1, nullptr, setData, nullptr);pthread_create(&t2, nullptr, processData, nullptr);// 等待线程结束pthread_join(t1, nullptr);pthread_join(t2, nullptr);// 销毁互斥锁和信号量pthread_mutex_destroy(&mutex);sem_destroy(&sem);return 0;
}
条件变量

条件变量提供了一种线程间的通知机制,当某个共享数据达到某个值时,唤醒等待这个共享数据的线程.

  • pthread_cond_init函数用于初始化条件变量
  • pthread_cond_destory函数销毁条件变量
  • pthread_cond_broadcast函数以广播的方式唤醒所有等待目标条件变量的线程
  • pthread_cond_wait函数用于等待目标条件变量.该函数调用时需要传入 mutex参数(加锁的互斥锁) ,函数执行时,先把调用线程放入条件变量的请求队列,然后将互斥锁mutex解锁,当函数成功返回为0时,互斥锁会再次被锁上. 也就是说函数内部会有一次解锁和加锁操作.

使用条件变量,结合互斥锁,来协调两个线程的操作,确保第一个线程(生产者)准备数据后,第二个线程(消费者)才开始处理数据。

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>// 全局共享数据
int ready = 0;// 互斥锁和条件变量
pthread_mutex_t mutex;
pthread_cond_t cond;// 生产者函数
void* producer(void* arg) {// 获取互斥锁pthread_mutex_lock(&mutex);printf("Producer is preparing data...\n");sleep(2); // 模拟数据准备时间ready = 1; // 标记数据已准备好printf("Producer has prepared data.\n");// 通知等待的线程(消费者),数据已准备好pthread_cond_broadcast(&cond);// 释放互斥锁pthread_mutex_unlock(&mutex);return NULL;
}// 消费者函数
void* consumer(void* arg) {// 获取互斥锁pthread_mutex_lock(&mutex);// 当数据还未准备好时,等待条件变量while (ready == 0) {printf("Consumer is waiting for data...\n");pthread_cond_wait(&cond, &mutex); // 等待条件变量,同时释放互斥锁}// 数据准备好后,继续执行printf("Consumer is processing data.\n");// 释放互斥锁pthread_mutex_unlock(&mutex);return NULL;
}int main() {pthread_t t1, t2;// 初始化互斥锁和条件变量pthread_mutex_init(&mutex, NULL);pthread_cond_init(&cond, NULL);// 创建生产者和消费者线程pthread_create(&t1, NULL, producer, NULL);pthread_create(&t2, NULL, consumer, NULL);// 等待线程结束pthread_join(t1, NULL);pthread_join(t2, NULL);// 销毁互斥锁和条件变量pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);return 0;
}

在条件变量被触发之前,线程不会执行,这有助于节省CPU资源,避免无效的轮询。

功能

锁机制的功能
  • 实现多线程同步,通过锁机制,确保任一时刻只能有一个线程能进入关键代码段.
封装的功能
  • 类中主要是Linux下三种锁进行封装,将锁的创建与销毁函数封装在类的构造与析构函数中,实现RAII机制
#ifndef LOCKER_H
#define LOCKER_H#include <semaphore.h>
#include <exception>
#include <pthread.h>// 信号量类,用于线程间的同步
class sem {
public:// 默认构造函数,初始化一个初始值为0的二进制信号量sem() {// 初始化一个仅在当前进程内共享的、初始值为 0 的二进制信号量,用于同步控制if (sem_init(&m_sem, 0, 0) != 0) {// 如果初始化失败,抛出异常throw std::exception();}}// 带初值的构造函数,初始化一个指定初值的二进制信号量sem(int num) {// 初始化一个仅在当前进程内共享的、初始值为 num 的二进制信号量if (sem_init(&m_sem, 0, num) != 0) {// 如果初始化失败,抛出异常throw std::exception();}}// 析构函数,销毁信号量~sem() {// 销毁信号量sem_destroy(&m_sem);}// 信号量等待操作,如果成功返回 truebool wait() {// 尝试获取信号量,如果成功则返回 true,否则返回 falsereturn sem_wait(&m_sem) == 0;}// 信号量发布操作,如果成功返回 truebool post() {// 发布信号量,如果成功则返回 true,否则返回 falsereturn sem_post(&m_sem) == 0;}
private:sem_t m_sem; // 内部信号量对象
};// 互斥锁类,用于线程同步
class locker {
public:// 构造函数:初始化互斥锁locker() {// 初始化互斥锁,如果初始化失败,则抛出异常if (pthread_mutex_init(&m_mutex, nullptr) != 0) {throw std::exception();}}// 析构函数:销毁互斥锁~locker() {// 销毁互斥锁pthread_mutex_destroy(&m_mutex);}// 尝试获取互斥锁bool lock() {// 如果成功获取互斥锁,则返回truereturn pthread_mutex_lock(&m_mutex) == 0;}// 尝试释放互斥锁bool unlock() {// 如果成功释放互斥锁,则返回true// 注意:这里错误地使用了pthread_mutex_destroy,应该是pthread_mutex_unlockreturn pthread_mutex_unlock(&m_mutex) == 0;}// 获取互斥锁的指针pthread_mutex_t *get() {// 返回互斥锁的指针return &m_mutex;}
private:pthread_mutex_t m_mutex; // 内部互斥锁
};// 条件变量类,提供线程同步机制
class cond {
public:// 构造函数,初始化条件变量cond() {// 初始化条件变量,如果初始化失败则抛出异常if (pthread_cond_init(&m_cond, nullptr) != 0) {throw std::exception();}}// 析构函数,销毁条件变量~cond() {// 销毁条件变量pthread_cond_destroy(&m_cond);}/*** @brief 等待条件变量* * @param m_mutex 用于锁定的互斥锁,确保等待时的互斥访问* @return true 等待成功* @return false 等待失败*/bool wait(pthread_mutex_t* m_mutex) {int ret = 0;// 等待条件变量,释放互斥锁并等待通知ret = pthread_cond_wait(&m_cond, m_mutex);return ret == 0;}/*** @brief 在限定时间内等待条件变量* * @param m_mutex 用于锁定的互斥锁,确保等待时的互斥访问* @param t 超时时间点* @return true 等待成功* @return false 等待失败或超时*/bool timewait(pthread_mutex_t* m_mutex, struct timespec t) {int ret = 0;// 在限定时间内等待条件变量,释放互斥锁并等待通知直到超时ret = pthread_cond_timedwait(&m_cond, m_mutex, &t);return ret == 0;}/*** @brief 发送信号唤醒等待条件变量的一个线程* * @return true 信号发送成功* @return false 信号发送失败*/bool signal() {// 唤醒等待条件变量的一个线程return pthread_cond_signal(&m_cond) == 0;}/*** @brief 广播唤醒所有等待条件变量的线程* * @return true 广播成功* @return false 广播失败*/bool broadcast() {// 唤醒所有等待条件变量的线程return pthread_cond_broadcast(&m_cond) == 0;}private:pthread_cond_t m_cond; // 条件变量
};#endif

http://www.ppmy.cn/news/1516806.html

相关文章

模板-C++

模板可以让你编写通用的、可重用的代码&#xff0c;而无需对每种数据类型编写重复的代码。模板分为两种主要类型&#xff1a;函数模板和类模板。 类模板 #include <iostream> #include <string> using namespace std;template< typename T> class PrintfEv…

网络安全售前入门03——审计类产品了解

目录 1.前言 2.堡垒机介绍 2.1产品架构功能 2.2应用场景 2.3部署形式 2.4产品价值 2.5选型依据 3.日志审计 3.1产品架构功能 3.2应用场景 3.3部署形式 3.4产品价值 3.5选型依据 后续 1.前言 为方便初接触网络安全售前工作的小伙伴了解网安行业情况,我制作一系统…

CodeQL 从零到精通第 3 部分:使用 CodeQL 进行安全研究

查询特定的库方法 在上一篇博文中,我们根据名称匹配函数调用、函数和方法调用,例如,在本[挑战](https://github.blog/2023-06-15-codeql-zero-to-hero-part-2-getting-started-with-codeql/#:~:text=Challenge 9—Find all functions with “command” as part of its name…

oc记录 - UIView.layer

UIView属性 layer 在 Objective-C 中&#xff0c;UIView 类有一个名为 layer 的属性&#xff0c;它返回一个 CALayer 对象。这个属性允许你直接操作视图的底层图形层&#xff0c;以便更精细地控制视图的外观和行为。 以下是 UIView 的 layer 属性的一些基本用法&#xff1a; …

使用LinkedHashMap实现固定大小的LRU缓存

使用LinkedHashMap实现固定大小的LRU缓存 1. 什么是LRU&#xff1f; LRU是"Least Recently Used"的缩写&#xff0c;意为"最近最少使用"。LRU缓存是一种常用的缓存淘汰算法&#xff0c;它的核心思想是&#xff1a;当缓存满时&#xff0c;优先淘汰最近最少…

在 FPGA 上实现以太网的“低级”指南

如今&#xff0c;我们日常的网络连接大多是通过无线方式进行的&#xff0c;因此很容易忘记以太网。但它仍然是一种有用的标准&#xff0c;是一个可靠的高吞吐量网络链接的好方法。为此&#xff0c;[Robert Feranec] 和 [Stacy Rieck] 编写了一个关于如何在 FPGA 上使用以太网的…

【Mysql】通过Keepalived搭建mysql双主高可用集群

一、环境信息 主机名ip操作系统mysql版本VIP&#xff08;虚拟ip&#xff09;hadoop01192.168.10.200centos7_x865.7192.168.10.253hadoop03192.168.10.202centos7_x865.7 二、mysql集群搭建 两台节点&#xff0c;如果未部署mysql服务&#xff0c;部署文档请看【Mysql】mysql…

模型 PMI思考法

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。全面评估&#xff0c;三思而后行。 1 PMI思考法的应用 1.1 个人职业发展中的PMI思考法应用 张华是一位有志于提升自己职业竞争力的职场人士。他正在考虑报名参加一个专业认证课程&#xff0c;以期提…

在我的博士科研生活中,SCI的英语写作一直是我的挑战。

在我的博士科研生活中&#xff0c;SCI的英语写作一直是我的挑战。尽管我不断地努力提高自己的语言水平&#xff0c;但每当我提交文章后&#xff0c;审稿人或编辑总是会指出语言表达的不足之处&#xff0c;让我深感苦恼。于是&#xff0c;我开始寻找专业的润色服务来帮助我提升文…

python如何判断回文

打开JUPTER NOTEBOOK&#xff0c;新建一个PYTHON文档。 n input("Please input string: ") print(n) 我们首先让用户输入要进行判断的字符串&#xff0c;然后打印出来查看一下。 n input("Please input string: ") is_palidrome n[::-1] if n is_palid…

单片机中的存储器讲解

单片机中的存储器 目录 单片机中的存储器常用的存储器易失性存储器RAMSRAMDRAM 非易失性存储器ROMMask ROMPROMEPROME2PROMFlashNOR FlashNADN Flash 单片机里全局变量、局部变量、堆、栈的存储区域区域介绍栈区堆区静态区代码区常量区 内存分区分类四个区域 常用的存储器 易失…

QT常用UI控件

目录 一、引言 二、QT常用UI控件概述 1.按钮&#xff08;QPushButton&#xff09; 2.文本框&#xff08;QLineEdit&#xff09; 3.标签&#xff08;QLabel&#xff09; 4.下拉列表&#xff08;QComboBox&#xff09; 5.单选按钮&#xff08;QRadioButton&#xff09; 6.复选框…

【OpenCV】SIFT(尺度不变特征变换)算法?

关键词&#xff1a;SIFT Algorithm 文章目录 一、介绍二、什么是 SIFT 算法&#xff1f;2.1 SIFT 算法描述2.2 SIFT 算法示例 三、人类与机器识别3.1 SIFT 在计算机视觉中的应用3.2 SIFT 关键点的优势3.3 示例演示 四、关键点定位关键点选择 五、实验任务指导5.1 计算大小和方…

博弈论详解 2(SG函数 和 SG定理)

传送门&#xff1a;博弈论详解 1&#xff08;基本理论定义 和 Nim 游戏&#xff09; 什么是 SG 函数 接着上次的讲解&#xff0c;我们来了解一个更通用的模型。我们把每一个状态变成一个点&#xff08;在 Nim 游戏里就代表 a a a 数组&#xff09;&#xff0c;如果可以从一种…

安装Win10操作系统时找不到任何驱动器的解决方法

安装Win10操作系统时找不到任何驱动器的解决方法 有时候在一台新电脑上使用U盘安装系统时提示&#xff1a;我们找不到任何驱动器。 如下图所示&#xff1a; 解决方法&#xff1a; 一、按F12&#xff08;不同电脑进入Bios的按键可能不同&#xff09;将电脑进入Bios画面&#xf…

DataX(Doris同步数据到SelectDB)

背景 由于之前的doris数仓在本地的服务器&#xff0c;当数据量越来越大&#xff0c;服务器的性能达不到要求&#xff0c;查询数据经常超时&#xff0c;故需要把本地的doris数仓部署到云上&#xff0c;本文以阿里云为例&#xff0c;迁移工具使用的阿里开源的datax。 datax官方文…

client网络模块的开发和client与server端的部分联动调试

客户端网络模块的开发 我们需要先了解socket通信的流程 socket通信 server端的流程 client端的流程 对于closesocket()函数来说 closesocket()是用来关闭套接字的,将套接字的描述符从内存清除,并不是删除了那个套接字,只是切断了联系,所以我们如果重复调用,不closesocket()…

20240828 每日AI必读资讯

8岁女孩玩转AI编程&#xff0c;45分钟打造聊天机器人&#xff0c;Karpathy都看呆了 - 新晋顶流AI代码编辑器——Cursor&#xff0c;已经进化到了“0手工代码”阶段。 - 提供了多个AI模型&#xff0c;包括GPT-4、GPT-4o和Claude 3.5 Sonnet等&#xff0c;可以通过跟大模型聊天…

微服务——远程调用

为什么需要远程调用&#xff1f; 在微服务架构中&#xff0c;每个服务都是独立部署和运行的&#xff0c;它们之间需要相互协作以完成复杂的业务逻辑。因此&#xff0c;远程调用成为微服务之间通信的主要方式。通过远程调用&#xff0c;一个服务可以请求另一个服务执行某些操作或…

【Python机器学习】NLP概述——词序和语法

词的顺序很重要&#xff0c;那些在词序列&#xff08;如句子&#xff09;中控制词序的规则被称为语言的语法&#xff08;也被称为文法&#xff09;。这是之前的词袋或词向量例子中所丢弃的信息。在大多数简短的短语甚至许多完整的句子中&#xff0c;上述词向量近似方法都可以奏…