C++多线程面向对象封装

2023-05-16

相信很多人都读过《C++沉思录》这本经典著作,在我艰难地读完整本书后,留给我印象最深的只有一句话::“用类表示概念,用类解决问题”。

关 于多线程编程,如果不是特别需要,大多数开发人员都不会特意去触碰这个似乎神秘的领域。如果在某些场合能正确并灵活地运用,多线程带来的好处是不言而喻 的。然而,任何事物都有两面性,如果程序中引入多线程,那么我们需要谨慎小心地处理许多与之相关的问题,其中最突出的就是:资源竞争、死锁和无限延迟。那 么面向对象与这些有什么关系了吗?有,面向对象的基础是封装,是的,正是封装,可以很好的解决多线程环境中的主要困境。

一.多线程环境

在开始之前,有必要先重温一下多线程环境。所谓,多线程编程,指的是在一个进程中有多个线程同时运行,每一个线程都有自己的堆栈,但是这些线程共享所有的全局变量和资源。

在引入互斥量之前,多线程的主要问题是资源竞争,所谓资源竞争就是两个或两个以上的线程在同一时间访问同一资源。为了解决这个问题,我们引入了互斥量,以同步多个线程对同一资源的访问。

然 而在引入互斥量之后,新的问题又来了。因为如果互斥量的获得和释放没有得到正确处理,就会引起严重的问题。比如,某线程获得互斥量后,就可以对受保护的资 源进行访问,但是如果访问完毕后,忘记了释放互斥量,那么其它的线程永远也无法访问那个受保护的资源了。这是一种较简单的情况,还有一种复杂的,那就是线 程1已经拥有了资源A,但是它要拥有资源B后才能释放A,而线程2了恰好相反,线程2已经拥有了资源B但是它要拥有资源A后才能释放B。这样一来,线程1和线程2就永远地相互等待,这就是所谓的死锁。

死 锁导致的问题是严重的,因为它使得程序无法正常运行下去。也许引入一些规范或约束有助于减少死锁发生的几率,比如我们可以要求,所有资源的访客(客户,使 用者)都必须在真正需要资源的时刻请求互斥量,而当资源一使用完毕,就立即释放它,另外,锁定与释放一定要是成对的。如果上面的线程1和线程2都遵守这个规范,那么上述的那种死锁情况就不会发生了。

然而,规范永远只是规范,规范能被执行多少要依赖于使用者的自觉程度有多高,这个世界上总是有对规范和约束视而不见的人存在。所以,我们希望能够强制执行类似的约束,在对使用者透明的情况下。对于约束的强制实施可以通过封装做到。

 

二.多线程面向对象解决方案

首先你需要将系统API封装成基础类,这样你就可以用面向对象的武器类对付多线程环境,二是将临界资源与对其的操作封装在一个类中。这两点的核心都是将问题集中在一个地方,防止它们泛滥在程序的各个地方。

 1.  将系统API封装成基础类。

 1.  将系统API封装成基础类。

厌倦了每次涉及共享资源操作时都需要调用InitializeCriticalSection、DeleteCriticalSection、EnterCriticalSection、LeaveCriticalSection,并且它们是成对使用的,如果你调用了EnterCriticalSection,却忘了调用LeaveCriticalSection,那么锁就永远得不到释放,并且这些API的使用是很不直观的。我喜欢将它们封装成类,只需封装一次,以后就不用再查MSDN,每个API怎么写的了,参数是什么,免去后顾之忧。而且,在类的构造函数中调用InitializeCriticalSection,析构函数中调用DeleteCriticalSection,可以防止资源泄漏。面向对象的封装真是个好东西,我们没有理由拒绝它。

来看看我封装的几个与多线程环境相关的基础类。

// CriticalSection类用于解决对临界资源的保护

class CriticalSection

{

protected:

       CRITICAL_SECTION critical_section ;

public:

       CriticalSection()

       {

              InitializeCriticalSection(&this->critical_section) ;

       }

       virtual ~CriticalSection()

       {

              DeleteCriticalSection(&this->critical_section) ;

       }

       void Lock()

       {

              EnterCriticalSection(&this->critical_section) ;

       }

       void Unlock()

       {

              LeaveCriticalSection(&this->critical_section) ;

       }

}; 

//Monitor用于解决线程之间的同步依赖

class Monitor

{

private:

HANDLE event_obj ;

public:

Monitor(BOOL isManual = FALSE)

{

        this->event_obj = CreateEvent(NULL ,FALSE ,isManual ,"NONAME") ;

}

~Monitor()

{

        //ReleaseEvent()

        CloseHandle(this->event_obj) ;

void SetIt()

{

        // 如果为auto,则SetEvent将event obj设为有信号,当一个等待线程release后,

        //event obj自动设为无信号

        //如果是manual,则release所有等待线程,且没有后面自动重设

        SetEvent(this->event_obj) ;

}

void ResetIt()

{    

        //手动将event obj设为无信号

        ResetEvent(this->event_obj) ;

void PulseIt()

{

        // 如果为auto,则PulseEvent将event obj设为有信号,当一个等待线程release后,

        //event obj自动设为无信号

        //如果是manual,PulseEvent将event obj设为有信号,且release所有等待线程,

        //然后将event obj自动设为无信号

        PulseEvent(this->event_obj) ;

DWORD Wait(long timeout)

{

        return WaitForSingleObject(this->event_obj ,timeout) ;

}

}; 

//Thread是对线程的简单封装

class Thread

{

private:    

HANDLE threadHandle ; 

unsigned long  threadId ;    

    unsigned long  exitCode ;

BOOL needTerminate ;

 public:

 public:

Thread(unsigned long exit_code = 0 )

{           

        this->exitCode = exit_code ;

        this->needTerminate = FALSE ;

}

 ~Thread(void)

 ~Thread(void)

{

        if(this->needTerminate)

        {           

               TerminateThread(this->threadHandle ,this->exitCode) ;

        }

long GetTheThreadID()

{

        return this->threadId ;

void Start(FunPtr pfn ,void* pPara)//启动线程

{

        this->threadHandle = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(pfn) ,pPara ,0,&(this->threadId));  

}

void SetTerminateSymbol(BOOL need_Terminate)

{

        this->needTerminate = need_Terminate ;

void wait(void)

{

        WaitForSingleObject(this->threadHandle,INFINITE) ; //用于阻塞宿主线程,使其不能早于本线程结束

}

}; 

        在大多数的多线程环境中,上述的几个类已经够用了,不如要实现更强劲的同步机制,你可以仿照上面自己进行封装。

2.  将临界资源与对其的操作封装在一个类中,如果这样做,锁的操作自动在类的实现中完成,而外部使用者不用关心是否处在多线程环境。也就是说这个类是线程安全的,在单线程和多线程环境下都可以使用。

比如我们经常需要使用线程安全的容器,我就自己封装了一个:

// SafeObjectList 线程安全的容器

#include <list>

#include "../Threading/CriticalSection.h"

 

plate<class T> class SafeObjectList : CriticalSection

{

private:

list<T> inner_list ;

list<T>::iterator itr ;

public:

void Add(T obj)

{

        this->Lock() ;

        this->inner_list.push_back(obj) ;

        this->Unlock() ;

}

void Remove(T obj)

{

       

        this->Lock() ;

        for(this->itr = this->inner_list.begin() ;this->itr != this->inner_list.end() ;this->itr++)

        {

               if(obj == (*(this->itr)))

               {

                      this->inner_list.erase(this->itr) ;

                      break ;

               }

        }

        this->Unlock() ;

       

void Clear()

{

        this->Lock() ;

        this->inner_list.clear() ;

        this->Unlock() ;

}

int Count()

{

        return (int)this->inner_list.size() ;

BOOL Contains(T& target)

{

        BOOL found = FALSE ; 

        this->Lock() ;

        for(this->itr = this->inner_list.begin() ;this->itr != this->inner_list.end() ;this->itr++)

        {

               if(target == (*(this->itr)))

               {

                      found = TRUE ;

                      break ;

               }

        }

        this->Unlock() ; 

        return found ; 

BOOL GetElement(int index ,T& result)

{

        BOOL succeed = FALSE  ;

        this->Lock() ;       

        if(index < (int)this->inner_list.size())

        {

               int i= 0 ;

               for(this->itr = this->inner_list.begin() ;this->itr != this->inner_list.end() ;this->itr++)

               {

                      if(i == index)

                      {

                             result =  (*this->itr) ;

                             break ;

                      }

                      i++ ;

               }    

              

               succeed = TRUE ;

        }

        this->Unlock() ;

       

        return succeed ;           

}    

};

 

       在 将临界资源与对其的操作封装在一个类中的时候,我们特别要需要注意的一点是封装的线程安全的方法(函数)的粒度,粒度太大则难以复用,粒度太小,则可能会 导致锁的嵌套。所以在封装的时候,一定要根据你的具体应用,视情况而定。我的经验是这样的,首先可以把粒度定小一点,但是一旦发现有锁的嵌套出现,就加大 粒度,把这两个嵌套合并成一个稍微粗一点粒度的方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++多线程面向对象封装 的相关文章

  • 用c语言写链表

    链表是数据结构的一种 xff0c 是其他三个数据结构栈 xff0c 树 xff0c 图的基础 xff0c 只有将链表这一数据结构弄懂 xff0c 才能理解其他三种数据结构 举一个例子 xff0c 老师让你设计一个联系人系统 xff0c 其中
  • Fiddler抓包工具详解

    Fiddler的详细介绍 一 Fiddler与其他抓包工具的区别 1 Firebug虽然可以抓包 xff0c 但是对于分析http请求的详细信息 xff0c 不够强大 模拟http请求的功能也不够 xff0c 且firebug常常是需要 无
  • python 解析Json对象之jsonpath_rw用法

    jsonpath rw xff1a 一个可以像写xpath一样写json的Python第三方库 首先安装 xff1a pip install jsonpath rw 实例 xff1a from jsonpath rw import json
  • selenium之xpath使用

    XPath即XML路径语言 xff0c 支持从xml或html中查找元素节点 xff0c 使用XPath完全可以替代其他定位放式 xff0c 如 xff1a find element by xpath 39 64 id 61 34 34 3
  • Python-面向对象之多态

    当子类和父类都存在相同的run 方法时 xff0c 我们说 xff0c 子类的run 覆盖了父类的run xff0c 在代码运行的时候 xff0c 总是会调用子类的run 这样 xff0c 我们就获得了继承的另一个好处 xff1a 多态 c
  • 使用Ubuntu帐户创建SFTP

    提供sftp服务的有vsftpd和internal sftp xff0c 这里用的是系统自带的internal sftp xff0c 操作步骤如下 xff1a 1 创建用户 testenv xff0c 并禁止ssh登录 xff0c 不创建家
  • flask数据分页paginate的使用(flask学习)

    Flask的数据分页示例 1 xff0c 首先写数据获取的视图函数 xff0c 就像这样 xff1a 64 app route 39 39 64 login required def index page 61 request args g
  • Python __dict__属性详解

    我们都知道Python一切皆对象 xff0c 那么Python究竟是怎么管理对象的呢 xff1f 1 无处不在的 dict 首先看一下类的 dict 属性和类对象的 dict 属性 coding utf 8 class A object 3
  • Flask-SQLAlchemy 中的 relationship & backref

    今天重看 Flask 时 xff0c 发现对backref仍然没有理解透彻 查阅文档后发现 xff0c 以前试图孤立地理解backref是问题之源 xff0c backref是与relationship配合使用的 一对多关系 db rela
  • Django HttpResponse与JsonResponse

    我们编写一些接口函数的时候 xff0c 经常需要给调用者返回json格式的数据 xff0c 那么如何返回可直接解析的json格式的数据呢 xff1f 首先先来第一种方式 xff1a from django shortcuts import
  • Ubuntu安装mysql

    首先执行下面三条命令 xff1a sudo apt get install mysql server sudo apt install mysql client sudo apt install libmysqlclient dev 安装成
  • 10种动态进度条用css3实现

    用css做的10种动态进度条 xff0c 喜欢可以直接去用话不多说先看效果图 xff1a 实现上图的 xff0c 最主要的就是应用了css动画属性 64 keyframes和animation属性结合应用 下面看看语法 xff1a 64 k
  • Yolo训练数据标注工具-Yolo_mark 使用教程

    一 安装与测试 环境 xff1a Ubuntu16 04 43 Opnecv 43 Cmake 项目地址 xff1a https github com AlexeyAB Yolo mark 下载 打开终端 xff0c 键入 xff1a gi
  • x86、ARM分属大小端

    小端模式 xff1a 一个数据的高位在大的地址端 xff0c 低位在小的地址端 xff0c x86也就是pc机就是小端的 xff1a include 34 stdio h 34 include 34 stdlib h 34 int main
  • 二叉树(C语言实现)——链式存储结构

    include lt stdio h gt include lt stdlib h gt include lt stdbool h gt define QueueSize 200 typedef char DataType typedef
  • 栈,堆,常量区都放什么

    1 寄存器 xff1a 最快的存储区 由编译器根据需求进行分配 我们在程序中无法控制 xff1b 1 栈 xff1a 存放基本类型的变量数据和对象的引用 xff0c 但对象本身不存放在栈中 xff0c 而是存放在堆 xff08 new 出来
  • Windows10安装Docker并创建本地Ubuntu环境

    安装Docker参考文章 xff1a https www cnblogs com Can daydayup p 15468591 html label0 安装本地Ubuntu环境 xff1a windows10下安装docker xff0c
  • 机器人操作系统ROS是什么?

    目录 1 什么是ROS 2 ROS的许可协议 3 ROS的主要发行版本 4 ROS的主要功能 5 ROS的应用 6 ROS开发的常用工具 7 ROS的优点 8 ROS的缺点 1 什么是ROS ROS是机器人操作系统 xff08 Robot
  • 【教程】如何移植FPGA关于HDMI例程

    教程 如何移植FPGA关于HDMI例程 时钟IP核约束条件 在完成EDA作业后 xff0c 抽空分享一下如何移植FPGA的例程 我EDA作业用的板子型号是Zybo Z7 xff0c 然后移植的是原子哥的HDMI实现方块移动例程 故本教程是基
  • 【MATLAB UAV Toolbox】使用指南(三)

    可视化自定义飞行日志 通过配置flightLogSignalMapping可从自定义的飞行日志中可视化数据 加载自定义的飞行日志 在本例中 xff0c 假设飞行数据已经被解析到MATLAB 中 xff0c 并存储为M文件 本示例重点介绍如何

随机推荐

  • matplotlib学习笔记

    matplotlib第一章 matplotlib通常有两种绘图接口 xff1a 显示创建figure和axes 依赖pyplot自动创建figure和axes 并绘图 matplotlib环境 本文是在jupyter notebook下运行
  • OPNET 修改节点图标大小

    老是记不住在哪修改图标 xff0c 有一天看急眼了 xff0c 经过半小时的斗争 xff0c 终于找到了 xff0c 这次一定要把它记下来 View gt Layout gt Scale Node icons Interactively
  • 自定义msg使用C++

    在之前创建talker的src文件夹中创建person cpp并编写如下 include 34 ros ros h 34 include 34 learning communication Person h 34 include lt ss
  • GPIO的八种模式分析

    GPIO是general purpose input output 即通用输入输出端口 xff0c 作用是负责外部器件的信息和控制外部器件工作 GPIO有如下几个特点 xff1a 1 不同型号的IO口数量不同 xff1b 2 xff0c 反
  • 关于STM32_IWDG独立看门狗的一些笔记

    独立看门狗 IWDG xff0c Independent watchdog xff0c 本质是一个可以定时产生系统复位信号 并且可以通过 喂狗 复位的计时器 它由独立的RC振荡器 低速时钟 LSI 驱动 xff0c 即使主时钟发生故障它也仍
  • 关于MPU的笔记

    MPU xff08 memory protection unit xff09 内存保护单元 这些系统必须提供一种机制来保证正在运行的任务不破坏其他任务的操作 即要防止系统资源和其他一些任务不受非法访问 嵌入式系统有专门的硬件来检测和限制系统
  • 关于OLED屏的笔记

    OLED即有机发光管 Organic Light Emitting Diode OLED OLED显示技术具有自发光 广视角 几乎无穷高的对比度 较低功耗 极高反应速度 可用于绕曲性面板 使用温度范围广 构造及制程简单等有点 xff0c 被
  • Ubuntu 上 Let‘s Encrypt 生成泛域名证书

    安装生成工具certbot xff1a apt install certbot 查看安装在哪 xff1a which certbot 使用certbot xff08 位置在 usr bin certbot xff09 生成证书 xff1a
  • DMA的补充笔记

    DMA有两个总线 xff1a 1 DMA存储器总线 xff1a DMA通过该总线来执行存储器数据的传入和传出 2 DMA外设总线 xff1a DMA通过该总线访问AHB外设 xff08 AHB主要是针对高效率 高频宽以及快速系统模块所设计的
  • 关于ADC的笔记1

    ADC xff0c 全称Anlog to Digital Converter xff0c 模拟 数字转换器 是指将连续变量的模拟信号转换为离散的数字信号的器件 xff0c 我们能通过ADC将外界的电压值读入我们的单片机中 常见的ADC有两种
  • STM32-ADC单通道采集实验

    实验要求 xff1a 通过ADC1通道 xff08 PA1 xff09 采集电位器的电压 xff0c 并显示ADC转换的数字量及换算后的电压值 首先要确定最小刻度 Vref 61 3 3V xff0c 所以输入电压有效范围在0V lt 61
  • jetson xavier nx安装ROS Melodic

    1 前期准备 打开系统设置 软件和更新 xff0c 确保图示的选项已选中 点击close xff0c 选择reload 在不同的教程里搜到的这一步都不同 xff0c 似乎没什么影响 xff0c 就很迷 2 设置你的源文件列表 设置计算机以接
  • sylixos标准工程移植到Lite版本

    1 概述 针对低端处理器 xff08 如ARM M系列处理器 xff09 的开发工作 xff0c 翼辉信息推出了SylixOS Lite工程版本 SylixOS Lite版本工程属于SylixOS轻量级工程 xff0c 与标准的SylixO
  • Linux下TCP/IP网络编程示例——实现服务器/客户端通信(一)

    一 说明 最近梳理网络编程的一些知识点时 xff0c 整理了一些笔记 xff0c 写了一些demo例程 xff0c 主要包含下面几部分 xff0c 后面会陆续完成 1 Linux下TCP IP网络编程示例 实现服务器 客户端通信 xff08
  • OpenMV色块寻找

    OpenMV入门 xff0c 从入门到入坟 gt lt 此文章大部分内容取自OpenMV官方中文参考文档 详情看OpenMV官方中文参考文档 文章目录 一 sensor snapshot 拍一张照片二 image find blogs 查找
  • 四轴 PID 调试

    四轴 PID 用到了串级 PID xff0c 即两个闭环 xff0c 分别为 角速度环 xff08 内环 xff09 和角度环 xff08 外环 xff09 调试时 xff0c 先整定内环PID xff0c 再整定外环 P 内环 P xff
  • bind:address already in use的深刻教训以及解决办法

    今天在linux下 xff0c 编写了一个简单的回射客户 服务器 xff08 就是客户机从控制台标准输入并发送数据 xff0c 服务端接受数据 xff0c 但是不对数据进行处理 xff0c 然后将数据返回 xff0c 交由客户机标准输出 x
  • stm32简说步进电机(有代码)!!!

    步进电机 xff08 也称脉冲电机 xff09 xff08 将电脉冲转换为相应的角位移或线位移的电磁机械装置 xff0c 具有快速启动 xff0c 停能力 xff0c 在电机的负荷不超过它能提供的动态转矩时 xff0c 可以通过输入脉冲来控
  • 阿里云Linux Ubentu16.04 安装 Nginx 并配置 https,后续升级openssl和Nginx

    一 准备工作 进入安装目录 cd usr local 下载nginx xff08 官网选择版本稳定版即可 xff1a http nginx org en download html xff09 wget http nginx org dow
  • C++多线程面向对象封装

    相信很多人都读过 C 43 43 沉思录 这本经典著作 xff0c 在我艰难地读完整本书后 xff0c 留给我印象最深的只有一句话 xff1a 用类表示概念 xff0c 用类解决问题 关 于多线程编程 xff0c 如果不是特别需要 xff0