【并发编程五】c++进程通信——信号量(semaphore)

news2025/8/17 2:26:42

【并发编程五】c++进程通信——信号量(semaphore)

  • 一、概述
  • 二、信号量
  • 三、原理
  • 四、过程
    • 1、进程A过程
    • 2、进程B过程
  • 五、demo
    • 1、进程A
    • 2、进程B
  • 六、输出
  • 七、windows api介绍
    • 1. 创建信号量 CreateSemaphore()
    • 2. 打开信号量 OpenSemaphore()
    • 3. 等待 WaitForSingleObject()
    • 4. 递增信号量的当前资源计数ReleaseSemaphore()
    • 5. 关闭句柄 CloseHandle()
  • 八、信号量使用场景

在这里插入图片描述

为了防⽌多进程竞争共享资源,⽽造成的数据错乱,所以需要保护机制,使得共享的资源,在任意时刻只能被⼀个进程访问(或者有限个进程访问)。正好,信号量就实现了这⼀保护机制。

一、概述

信号量的概念是由荷兰计算机科学家艾兹赫尔·戴克斯特拉(Edsger W. Dijkstra)发明的,广泛的应用于不同的操作系统中。在系统中,给予每一个进程一个信号量,代表每个进程目前的状态,未得到控制权的进程会在特定地方被强迫停下来,等待可以继续进行的信号到来。如果信号量是一个任意的整数,通常被称为计数信号量(Counting semaphore),或一般信号量(general semaphore);如果信号量只有二进制的0或1,称为二进制信号量(binary semaphore)

  • 信号量是操作系统提供的一种协调共享资源访问的方法。信号量则由操作系统进行管理,地位高于进程,操作系统保证信号量的原子性。

二、信号量

信号量(英语:semaphore)又称为信号标或者信号灯,是一个同步对象,用于保持在0至指定最大值之间的一个计数值。当线程完成一次对该semaphore对象的等待(wait)时,该计数值减一;当线程完成一次对semaphore对象的释放(release)时,计数值加一。当计数值为0,则线程等待该semaphore对象不再能成功直至该semaphore对象变成signaled状态。semaphore对象的计数值大于0,为signaled状态;计数值等于0,为nonsignaled状态。

三、原理

一个信号量 S 是个整型变量,它除了初始化外只能通过两个标准原子操作:wait () 和 signal() 来访问:

  • 操作 wait() 最初称为 P(荷兰语proberen,测试);成功则,S–;
  • 操作 signal() 最初称为 V(荷兰语verhogen,增加);成功则,S++;

四、过程

我们以windows api的接口为例,讲解下信号量是如何在进程A和进程B间做到进程间同步的。

1、进程A过程

  • 1.1、CreateSemaphore():创建一个名字为Semaphore的信号量,该信号量初始可使用的资源数为0。即S=0.
  • 1.2、WaitForSingleObject():等待信号量>0,就是等待信号量的资源数大于0时。成功后就是S–。(启动进程A后,此处会一直等待,因为创建的信号量初始的值=0,直到进程B打开进程A的信号量,并且释放一个可以使用的资源时,S变成1,才可以继续,进行后面的程序
  • 1.3、在屏幕打印文字。
  • 1.4、ReleaseSemaphore():释放上面wait成功时占用的1个资源数。执行成功后就是S++。
  • 1.5、等待5s。

2、进程B过程

  • 2.1、OpenSemaphore():打开进程A创建的信号量,名字为Semaphore
  • 2.2、ReleaseSemaphore():递增信号量的当前资源计数,就是S++。S=1
  • 2.3、WaitForSingleObject():等待信号量>0,就是等待信号量的资源数大于0时。成功后就是S–。
  • 2.4、在屏幕打印文字。
  • 2.5、ReleaseSemaphore():释放上面wait成功时占用的1个资源数。成功后就是S++。
  • 2.6、等待5s。

五、demo

1、进程A

  • main.cpp
#include <windows.h>
#include <iostream> 
using namespace std;

#define BUF_SIZE 4096

int main(int argc, TCHAR* argv[])
{
    cout << "processA:" << endl << endl;

    // 创建信号量
    HANDLE handle = CreateSemaphore(
        NULL,
        0,
        10,
        "Semaphore"
    );

    cout << "创建信号量成功,并等待进程B释放一个可以使用的资源数......." << endl<<endl;
    for (int i = 0; i < 5; i++)
    {
        cout << "进入新号量等待区" << endl;
        DWORD ret = WaitForSingleObject(
            handle,
            100000
        );
        if (0 == ret)
        {
            cout << "processA:" << i << ".....s--" << endl;
        }
        ReleaseSemaphore(handle , 1,NULL);
        cout << "释放1个信号并等待5秒中....." << "s++" << endl << endl;
        Sleep(5000);
    } 
    system("pause"); //等待其他进程读取数据

    // 关闭句柄
    CloseHandle(handle);
    return 0;
}

  • CmakeList.txt
CMAKE_MINIMUM_REQUIRED(VERSION 3.8.0)
PROJECT(process)
ADD_EXECUTABLE(processA main.cpp)
ADD_SUBDIRECTORY(processB)
SET(EXECUTABLE_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/lib")

2、进程B

  • main.cpp
#include <iostream>  
#include <windows.h>  
using namespace std;

#define BUF_SIZE 4096

int main()
{
    cout << "processB:" << endl << endl;

     // 打开共享的文件对象
    HANDLE handle = OpenSemaphore(
    SEMAPHORE_ALL_ACCESS,          // 访问标志
    TRUE,                          // 继承标志
    "Semaphore");                   // 信号量名
    if (nullptr != handle)
    {
        cout << "打开信号量:成功" << endl;
    }
    else
    {
        cout << "打开信号量:失败" << endl;
    }

    cout << "释放1个可以使用的资源数:s++" << endl<<endl;
    ReleaseSemaphore(handle, 1, NULL);
    
    for (int i = 0; i < 5; i++)
    {
        cout << "进入新号量等待区" << endl;
        DWORD ret = WaitForSingleObject(
            handle,
            100000
        );
        if (0 == ret)
        {
            cout << "processB:" << i <<".....s--"<< endl;
        }
        ReleaseSemaphore(handle, 1, NULL);
        cout << "释放1个信号并等待5秒中....." << "s++" << endl << endl;
        Sleep(5000);

    }

    system("pause");
    return 0;
}

  • CmakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 3.8.0)

SET(TARGET "childprocess")

ADD_EXECUTABLE(processB main.cpp)

SET(LIBRARY_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/lib")
SET(EXECUTABLE_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/lib")

六、输出

构建、编译、先启动进程A,输出如下。(备:因为创建信号量时,初始化资源数为0,即S=0,如果你不启动进程B释放可用的资源数,进程A在此处会等待直到超时)

在这里插入图片描述

再启动进程B输出如下。

在这里插入图片描述

七、windows api介绍

本文我们以windows的api接口为例,其实Linux下接口原理是一样的,用法也类似,我们不再过多介绍。

1. 创建信号量 CreateSemaphore()

函数说明:

第一个参数表示安全控制,一般直接传入NULL。
第二个参数表示初始资源数量。
第三个参数表示最大并发数量。
第四个参数表示信号量的名称,传入NULL表示匿名信号量。

HANDLE WINAPI CreateSemaphore(  
  _In_opt_  LPSECURITY_ATTRIBUTES lpEventAttributes,   // 安全属性,通常为NULL
  _In_      LONG lInitialCount,                        // 可用资源数目,就是信号量S的初始值
  _In_      LONG lMaximumCount,                        // 信号量对象可处理的最大资源数,就是S的最大值
  _In_opt_  LPCTSTR lpName                             // 信号量的名称,进程之间可共享
);  

2. 打开信号量 OpenSemaphore()

函数说明:

第一个参数表示访问权限,对一般传入SEMAPHORE_ALL_ACCESS。详细解释可以查看MSDN文档。
第二个参数表示信号量句柄继承性,一般传入TRUE即可。
第三个参数表示名称,不同进程中的各线程可以通过名称来确保它们访问同一个信号量。

HANDLE WINAPI OpenSemaphore(  
  _In_  DWORD dwDesiredAccess,   // 访问的权限
  _In_  BOOL bInheritHandle,     // 子进程继承信号量对象与否
  _In_  LPCTSTR lpName           // 信号量对象名称
); 

3. 等待 WaitForSingleObject()

等待指定对象处于信号状态或超时间隔已过。
本文中,就是等待信号量S>0时,进入。如果信号量S<=0,那就是等待,直到设置的超时时间。

DWORD WINAPI WaitForSingleObject(  
  _In_  HANDLE hHandle,                   // 内核对象句柄
  _In_  DWORD dwMilliseconds              // 单位,毫秒。对象被触发前的等待时间,INFINITE表示无限时间阻塞等待,简单来说就是死等
);  

4. 递增信号量的当前资源计数ReleaseSemaphore()

函数说明:

第一个参数是信号量的句柄。
第二个参数表示增加个数,必须大于0且不超过最大资源数量。
第三个参数可以用来传出先前的资源计数,设为NULL表示不需要传出。

BOOL WINAPI ReleaseSemaphore(  
  _In_       HANDLE hSemaphore,    // 信号量对象句柄
  _In_       LONG lReleaseCount,   // 释放使用的资源树
  _Out_opt_  LPLONG lpPreviousCount   // 一般设为null
);  

当一个线程使用完信号量对象控制的有限资源后,应该调用ReleaseSemaphore,释放使用的资源,使信号量对象的当前资源计数得到恢复

5. 关闭句柄 CloseHandle()

BOOL WINAPI CloseHandle(  
  _In_  HANDLE hObject    // 句柄
); 

八、信号量使用场景

  • 多进程访问统一资源。
  • 资源限流。
    比如:数据库连接池,同时进行连接的线程有数量限制,连接不能超过一定的数量,当连接达到了限制数量后,后面的线程只能排队等前面的线程释放了数据库连接才能获得数据库连接。

关于资源限流,我们举例说下。
1、一个厕所有3个坑,进厕所需要拿1个号牌(5毛钱一个号牌)。
2、肯定要设置为信号量的初始值为S=3,就是厕所的号牌数量为3。
3、进去一个人,号牌减1,S–。
4、当进入3个人时,没有号牌了,S=0,第四个人就要等待了,
5、等待厕所中的其中一个人上完大号出来,把号牌交出去,S++,其他人才可以拿着号牌进去。
(其实,你想想网站服务器控制并发访问和限流,不也是这个原理吗?)

  • 参考
    1、多线程面试题系列(8):经典线程同步 信号量Semaphore
    2、 信号量与管程
    3、 信号量及其使用和实现(超详细)
    4、 维基百科-信号量
    4、 信号量限流,高并发场景不得不说的秘密

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

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

相关文章

一种基于IO口的模拟串口(LOG)实现方法

一、使用背景 当MCU的串口不够用时&#xff0c;可以通过IO模拟的方式将任意一个具有输出功能的管脚配置为串口输出&#xff0c;从而方便开发和调试。 二、实现原理 通过IO口模拟串口发送波形&#xff0c;配置对应的波特率等信息&#xff0c;然后映射printf函数&#xff0c;从…

基于粒子群优化算法的冷热电联供型综合能源系统运行优化(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

redis 支持的数据类型

Redis 数据库支持五种数据类型。 字符串&#xff08;string&#xff09; 哈希&#xff08;hash&#xff09; 列表&#xff08;list&#xff09; 集合&#xff08;set&#xff09; 有序集合&#xff08;sorted set&#xff09; 位图 ( Bitmaps ) 基数统计 ( HyperLogLogs ) 字…

Vue3.2 + Element-Plus 二次封装 el-table(Pro版)

前言 &#x1f4d6; ProTable 组件目前已是 2.0版本&#x1f308;&#xff0c;在 1.0版本 中大家提出的问题与功能优化&#xff0c;目前已经得到优化和解决。 &#x1f600; 欢迎大家在使用过程中发现任何问题或更好的想法&#xff0c;都可以在下方评论区留言&#xff0c;或者我…

【计算机网络】局域网体系结构、以太网Ethernet详解

注&#xff1a;最后有面试挑战&#xff0c;看看自己掌握了吗 文章目录局域网LAN决定局域网的要素网络拓扑传输介质局域网的分类以太网令牌环网FDDI网----Fiber Distributed Data InterfaceATM网---Asynchronous Transfer Mode无线局域网WLAN----Wireless Local Area NetworkMAC…

Red Hat Enterprise Linux (RHEL) 9 更新了哪些新特性?

文章目录1. 前言2. 软件3. 支持的硬件架构4. GNOME更新到40版5. 安全和身份6. 构建容器的通用基础镜像7. 改进了用于管理 RHEL 9 的 Cockpit Web 控制台1. 前言 体验一下最新的rhel 9.0 是什么感觉。它会飞吗&#xff1f; Red Hat Enterprise Linux (RHEL) 9现已普遍可用 (GA…

吃柿子的禁忌靠谱吗?

图片来源&#xff1a;pixabay 秋冬是柿子上市的季节&#xff0c;虽然柿子并不是苹果、香蕉这样的大宗水果&#xff0c;但是秋天不吃个柿子&#xff0c;冬天不吃个柿饼&#xff0c;总觉得少了点什么。 关于吃柿子有很多禁忌&#xff0c;比如说柿子不能与螃蟹同时吃&#xff0c;柿…

​怎么保留硬盘数据合并分区 ,如何才能合并且不丢失数据

硬盘分区合并是比较常见的操作&#xff0c;​怎么保留硬盘数据合并分区&#xff0c;还是具有一定的难度。因为在Windows操作系统中&#xff0c;用户可以通过磁盘管理来实现硬盘分区合并&#xff0c;但是要删除该磁盘分区右侧的相邻分区&#xff0c;但是对于部分不懂计算机的用户…

Tailscale的子网路由和出口节点

2 年前&#xff0c;老苏写了 『 外网访问群晖的新方案Tailscale 』&#xff0c;第一次隆重的给大家推荐了 Tailscale&#xff0c;但当时还有很多功能并不具备&#xff0c;比如今天要介绍的 Subnet Router 和 Exit Node 【特别说明】&#xff1a;老苏使用的是DSM6 &#xff0c;所…

RabbitMQ初步到精通-第一章-消息中间件介绍

第一章 消息中间件介绍 1.MQ概述 MQ全称是Message Queue&#xff0c;消息的队列&#xff0c;因为是队列&#xff0c;所以遵循FIFO 先进先出的原则&#xff0c;它是一种跨进程的通信机制&#xff0c;用于上下游传递消息。 在互联网架构中&#xff0c;MQ是一种非常常见的上下游“…

论文阅读笔记 | 三维目标检测——VeloFCN算法

如有错误&#xff0c;恳请指出。 文章目录paper&#xff1a;《Vehicle Detection from 3D Lidar Using Fully Convolutional Network》 对于64线激光雷达全范围扫描出来的点云进行特征图的构建。对于具体的点&#xff08;xyz坐标&#xff09;&#xff0c;其在水平方向上可以通…

一个是证书服务和web安全访问配置,一个是PGP的使用

一个是证书服务和web安全访问配置&#xff0c;一个是PGP的使用 IIS介绍 IIS是本机自带的服务&#xff0c;用于上线web网页&#xff1b;虽然是自带但因为非开发人员用不到&#xff0c;所以属于预安装&#xff1b;在本机搜索下载即可&#xff0c; 打开后 证书服务&#xff0c;认…

LeetCode[105]从前序与中序遍历序列构造二叉树

难度&#xff1a;中等 题目&#xff1a; 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 示例 1: 输入: preorder [3,9,20,15,7], inorder [9,3,1…

Vue基础4

Vue基础4计算属性姓名案例 - 第一种用click.keyup的方法姓名案例 - 第二种用v-model双向绑定的方法姓名案例 - 第三种使用methods方法姓名案例 - 第四种使用计算属性的方法计算属性的简写—只考虑读取&#xff0c;不考虑修改时候使用监视属性第一种普通写法第二种用计算属性的写…

【信号处理】卡尔曼(Kalman)滤波(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

Java内部类分类

文章目录内部类分类局部内部类的使用匿名内部类成员内部类静态内部类一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。是我们类的第五大成员 思考:类的五大成员是哪些? - 属性、方法、构造器、代码块、内…

Windows安装Git教程(2022.11.18 Git2.38.1)

&#xff08;1&#xff09;首先前往Git官网&#xff0c;下载安装文件&#xff1a; &#xff08;2&#xff09;打开安装程序&#xff0c;把Only show new options的勾去掉&#xff0c;点击Next&#xff1a; &#xff08;3&#xff09;此处可以选用默认设置&#xff0c;也可以勾…

ProCAST一键导出有限元模型的几何拓扑和属性信息

第一次将ProCast有限元后处理中的数据导出&#xff0c;当时没有经验&#xff0c;方法比较粗暴&#xff0c;详情见文章&#xff1a;ProCast导出节点应力数据并格式化。 最近发现了一种更高效的数据导出“新姿势”&#xff0c;能够快速得到有限元模型的几何拓扑和节点属性数据&a…

电科大离散数学-2-命题逻辑-2

目录 2.7 范式 2.7.1 范式的定义 2.7.2 范式存在定理 2.8 主析取范式和主合取范式 2.8.1 极小项和极大项的定义和编码 2.8.2 极小项和极大项的性质 2.8.3 主析取范式和主合取范式的定义 2.8.4 主范式求解定理 2.8.5 真值表技术 2.8.6 范式的相互转化 2.8.7 主范式的…

[附源码]Python计算机毕业设计jspm计算机学院党员积分管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…