C# 实现条件变量

C# 进程通信系列

第一章 共享内存
第二章 条件变量(本章)
第三章 消息队列


文章目录

  • C# 进程通信系列
  • 前言
  • 一、关键实现
    • 1、用到的主要对象
    • 2、初始化区分创建和打开
    • 3、变量放到共享内存
    • 4、等待和释放逻辑
  • 二、完整代码
  • 三、使用示例
    • 1、线程同步控制
    • 2、进程同步控制
  • 总结


前言

C#提供的多进程同步对象有互斥锁和信号量,但是并没有条件变量。虽然信号量条件变量一定程度可以等效,但是具体的使用还是会有区别。比如说消息队列用条件变量就比信号量方便,用信号量要么缺乏灵活性,要么辅助代码已经和实现一个条件变量没区别了。本文提供了一种条件变量的实现方法,可以用于进程间的同步控制。


一、关键实现

1、用到的主要对象

下列对象都是跨进程的

//互斥变量,
Mutex _mtx;
//等待发送信号量
Semaphore _waitSem;
//等待完成信号量
Semaphore _waitDone;
//共享内存,用于存储计数变量
MemoryMappedFile _mmf;
//共享内存的读写对象
MemoryMappedViewAccessor _mmva;

2、初始化区分创建和打开

利用Mutex判断是创建还是打开

bool isCreateNew;
_mtx = new Mutex(false, name, out isCreateNew);if(isCreateNew){//只能在创建时,初始化共享变量}

3、变量放到共享内存

条件变量需要的计算对象就两个Waiting、Signals表示等待数和释放数。

//放到共享内存的数据
struct SharedData
{public int Waiting;public int Signals;
}
SharedData Data
{set{_mmva.Write(0, ref value);}get{SharedData ret;_mmva.Read(0, out ret);return ret;}
}

4、等待和释放逻辑

参考了SDL2的条件变量实现,具体略。有兴趣的朋友可以自行查找源码查看。


二、完整代码

using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;namespace AC
{/************************************************************************* @Project:  	AC::ConditionVariable* @Decription:   条件变量*                支持跨进程* @Verision:  	v1.0.0    *              更新日志*              v1.0.0:实现基本功能*              v1.1.0:优化进程内逻辑* @Author:  	Xin* @Create:  	2024/07/18 15:25:00* @LastUpdate:  2024/07/21 20:53:00************************************************************************* Copyright @ 2025. All rights reserved.************************************************************************/class ConditionVariable : IDisposable{/// <summary>/// 构造方法/// 本进程内使用条件变量,会更轻量一些。/// </summary>public ConditionVariable(){bool isCreateNew;Initialize(null, out isCreateNew);}/// <summary>/// 构造方法/// </summary>/// <param name="name">唯一名称,系统级别,不同进程创建相同名称的本对象,就是同一个条件变量。</param>public ConditionVariable(string name){bool isCreateNew;Initialize(name, out isCreateNew);}/// <summary>/// 构造方法/// </summary>/// <param name="name">唯一名称,系统级别,不同进程创建相同名称的本对象,就是同一个条件变量。</param>/// <param name="isCreateNew">表示是否新创建,是则是创建,否则是打开已存在的。</param>public ConditionVariable(string? name, out bool isCreateNew){Initialize(name, out isCreateNew);}/// <summary>/// 等待/// </summary>/// <param name="outerMtx">外部锁</param>public void WaitOne(Mutex outerMtx){WaitOne(Timeout.InfiniteTimeSpan, outerMtx);}/// <summary>/// 等待超时/// </summary>/// <param name="timeout">超时时间</param>/// <param name="outerMtx">外部锁,可以跨进程使用</param>/// <returns>是则成功,否则超时</returns>public bool WaitOne(TimeSpan timeout, Mutex outerMtx){return WaitOne(timeout, (object)outerMtx);}/// <summary>/// 等待/// </summary>/// <param name="outerMtx">外部锁,为lock关键字的对象,只能本进程使用</param>public void WaitOne(object outerMtx){WaitOne(Timeout.InfiniteTimeSpan, outerMtx);}/// <summary>/// 等待超时/// </summary>/// <param name="timeout">超时时间</param>/// <param name="outerMtx">外部锁</param>/// <returns>是则成功,否则超时</returns>public bool WaitOne(TimeSpan timeout, object outerMtx){bool isNotTimeout;//记录等待数量if (_mtx != null) _mtx.WaitOne(); else Monitor.Enter(_waitSem);var ws = Data;ws.Waiting++;Data = ws;if (_mtx != null) _mtx.ReleaseMutex(); else Monitor.Exit(_waitSem);//解除外部的互斥锁,让其他线程可以进入条件等待。if (outerMtx is Mutex) ((Mutex)outerMtx).ReleaseMutex(); else Monitor.Exit(outerMtx);//等待信号isNotTimeout = _waitSem.WaitOne(timeout);if (_mtx != null) _mtx.WaitOne(); else Monitor.Enter(_waitSem);ws = Data;if (isNotTimeout && ws.Signals > 0){//通知发送信号的线程,等待完成。_waitDone.Release();ws.Signals--;}ws.Waiting--;Data = ws;if (_mtx != null) _mtx.ReleaseMutex(); else Monitor.Exit(_waitSem);//加上外部互斥锁,还原外部的锁状态。if (outerMtx is Mutex) ((Mutex)outerMtx).WaitOne(); else Monitor.Enter(outerMtx);return !isNotTimeout;}/// <summary>/// 释放,通知/// </summary>public void Release(){if (_mtx != null) _mtx.WaitOne(); else Monitor.Enter(_waitSem);var ws = Data;if (ws.Waiting > ws.Signals){ws.Signals++;Data = ws;_waitSem.Release();if (_mtx != null) _mtx.ReleaseMutex(); else Monitor.Exit(_waitSem);_waitDone.WaitOne();}else{if (_mtx != null) _mtx.ReleaseMutex(); else Monitor.Exit(_waitSem);}}/// <summary>/// 释放全部,广播/// </summary>public void ReleaseAll(){if (_mtx != null) _mtx.WaitOne(); else Monitor.Enter(_waitSem);var ws = Data;if (ws.Waiting > ws.Signals){int waiting = ws.Waiting - ws.Signals;ws.Signals = ws.Waiting;Data = ws;_waitSem.Release(waiting);if (_mtx != null) _mtx.ReleaseMutex(); else Monitor.Exit(_waitSem);_waitDone.WaitOne(waiting);}else{if (_mtx != null) _mtx.ReleaseMutex(); else Monitor.Exit(_waitSem); _mtx.ReleaseMutex();}}/// <summary>/// 销毁对象,只会销毁当前实例,如果多个打开同个名称,其他对象不受影响/// </summary>public void Dispose(){_mtx?.Dispose();_waitSem.Dispose();_waitDone.Dispose();_mmva?.Dispose();_mmf?.Dispose();}void Initialize(string? name, out bool isCreateNew){Mutex? mtx = null;Semaphore? waitSem = null;Semaphore? waitDone = null;MemoryMappedFile? mmf = null;MemoryMappedViewAccessor? mmva = null;try{if (name != null){mtx = _mtx = new Mutex(false, name, out isCreateNew);_mtx.WaitOne();try{waitSem = _waitSem = new Semaphore(0, int.MaxValue, name + ".cv.ws");waitDone = _waitDone = new Semaphore(0, int.MaxValue, name + ".cv.wd");var _shmPath = Path.Combine(_TempDirectory, name + ".cv");mmf = _mmf = MemoryMappedFile.CreateFromFile(File.Open(_shmPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite), null, Marshal.SizeOf<SharedData>(), MemoryMappedFileAccess.ReadWrite, HandleInheritability.Inheritable, false);mmva = _mmva = _mmf.CreateViewAccessor();if (isCreateNew) Data = new SharedData() { Signals = 0, Waiting = 0 };}finally{_mtx.ReleaseMutex();}}else{waitSem = _waitSem = new Semaphore(0, int.MaxValue);waitDone = _waitDone = new Semaphore(0, int.MaxValue);isCreateNew = true;Data = new SharedData() { Signals = 0, Waiting = 0 };}}catch{mtx?.Dispose();waitSem?.Dispose();waitDone?.Dispose();mmf?.Dispose();mmva?.Dispose();isCreateNew = false;throw;}}Mutex? _mtx;Semaphore _waitSem;Semaphore _waitDone;MemoryMappedFile ?_mmf;MemoryMappedViewAccessor ?_mmva;SharedData _data;struct SharedData{public int Waiting;public int Signals;}SharedData Data{set{if (_mmva != null){_mmva.Write(0, ref value);}else{_data = value;}}get{if (_mmva != null){SharedData ret;_mmva.Read(0, out ret);return ret;}return _data;}}static string _TempDirectory = Path.GetTempPath() + "EE3E9111-8F65-4D68-AB2B-A018DD9ECF3C";}
}

三、使用示例

1、线程同步控制

using AC;
ConditionVariable cv = new ConditionVariable();
string text = "";
//子线程发送消息
new Thread(() =>
{int n = 0;while (true){lock(cv){text = (n++).ToString();//通知主线程cv.Release();}}}).Start();
//主线程接收消息
while (true)
{lock(cv){//等待子消息cv.WaitOne(cv);Console.WriteLine(text);}
}

在这里插入图片描述

2、进程同步控制

进程A

//不同进程名称相同就是同一个对象
ConditionVariable cv = new ConditionVariable("cv1");
Mutex mutex = new Mutex(false,"mx1");
//进程A发送消息
while (true)
{mutex.WaitOne();//共享内存写略//通知进程Bcv.Release();mutex.ReleaseMutex();
}

进程B

//不同进程名称相同就是同一个对象
ConditionVariable cv = new ConditionVariable("cv1");
Mutex mutex = new Mutex(false,"mx1");
//进程B接收消息
while (true)
{mutex.WaitOne();//等待进A程消息cv.WaitOne(mutex);//共享内存读略Console.WriteLine("收到进程A消息");mutex.ReleaseMutex();
}

在这里插入图片描述


总结

以上就是今天要讲的内容,之所以实现这样一个对象是因为,笔者在写跨进程队列通信,用信号量实现发现有所局限,想要完善与重写一个条件变量差异不大,索性直接实现一个条件变量,提供给队列使用,同时还具体通用性,在其他地方也能使用。总的来说,条件变量还是有用的,虽然需要遇到相应的使用场景才能意识到它的作用。

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

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

相关文章

ROS配置并同时驱动多个UVC相机(含功能包)

配置并同时驱动多个UVC相机&#xff0c;并将数据保存为ROS话题形式的bag文件。 ROS可以同时驱动多个UVC相机。要实现这个目标并将数据保存成ROS话题的形式&#xff0c;再保存为bag文件&#xff0c;可以按照以下步骤操作&#xff1a; 1. 安装必要的包 sudo apt-get update sud…

MySQL零散拾遗(四)--- 使用聚合函数时需要注意的点点滴滴

聚合函数 聚合函数作用于一组数据&#xff0c;并对一组数据返回一个值。 常见的聚合函数&#xff1a;SUM()、MAX()、MIN()、AVG()、COUNT() 对COUNT()聚合函数的更深一层理解 COUNT函数的作用&#xff1a;计算指定字段在查询结果中出现的个数&#xff08;不包含NULL值&#…

ElMessage自动引入,样式缺失和ts esline 报错问题解决

一. 环境 "unplugin-auto-import": "^0.17.6", "vue": "^3.3.8", "vite": "^5.0.0", "typescript": "^5.2.2",二. ElMessage样式缺失问题. 以下有两种解决方法 方法一: 配置了自动引用后…

Oracle集群RAC磁盘管理命令asmcmd的使用

文章目录 ASM磁盘共享简介ASM磁盘共享的优势ASM磁盘组成ASM磁盘共享的应用场景Asmcmd简介Asmcmd的功能Asmcmd的命令Asmcmd的使用注意事项Asmcmd运行模式交互模式运行非交互模式运行ASMCMD命令分类实例管理命令:文件管理命令:磁盘组管理命令:模板管理命令:文件访问管理命令:…

Python文献调研(一)环境搭建

一、安装Python版本 1.点击进入Python官网 Download Python | Python.org 2.根据自己的需求选择python的版本&#xff0c;点击【Download】 3.自定义安装路径&#xff0c;记得勾选Add Python xxx to PATH 这步是自动配置环境变量的&#xff0c;如果忘记勾选&#xff0c;建议…

VirtualBox 安装Centos 7 避坑指南 SSH连不上 镜像失效 静态网络配置等

背景 几乎每次安装Centos 7 时&#xff0c;都会遇到各种各样的问题&#xff0c;毕竟每次安装动辄就是半年几年&#xff0c;几乎都是在换工作时&#xff0c;有了新机器才会倒腾一次&#xff0c;时间久远&#xff0c;就会忘记一些细节&#xff0c;这次整理一下&#xff0c;避免以…

如何定位线上OOM

造成OOM的原因 1一次性申请太多对象。如&#xff1a;从数据库获取大量数据。 解决方法&#xff1a;更改申请对象的数量。如&#xff1a;做个分页。 2内存资源使用完未释放。如&#xff1a;太多线程建立数据库连接而未释放。 解决方法&#xff1a;使用线程池。 3本身资源不够…

Linux---01---安装VMware

一. 什么时Linux Linux 是一个开源的类 Unix 操作系统,Linux 是许多计算机硬件的底层操作系统&#xff0c;特别是服务器、嵌入式系统和个人电脑。它支持多种架构&#xff0c;包括 x86、x64、ARM 和 MIPS 等。Linux 因其稳定性、安全性、开源性以及广泛的社区支持而广受欢迎。 …

如何压缩视频大小不改变画质?这5个视频压缩免费软件超好用!

如何压缩视频大小不改变画质&#xff1f;随着生活的水平逐步提高&#xff0c;视频流媒体服务越来越受欢迎。提供简短而引人注目的视频来展示您的产品或服务已成为一种出色的营销手段。然而&#xff0c;当您要准备导出最终视频时&#xff0c;可能会面临一个常见问题&#xff1a;…

小规模的LLMS

对于小模型来说&#xff0c;训练目标已经改变。关键问题是&#xff0c;AI系统如何从更少的数据中学到更多 我们需要模型先变得更大&#xff0c;再变得更小&#xff0c;因为我们需要「巨兽」将数据重构、塑造为理想的合成形式&#xff0c;逐渐得到「完美的训练集」&#xff0c;…

算法之递归算法

递归是非常常见的一种算法&#xff0c; 也比较难以理解&#xff0c;简而言之&#xff0c;递归就是写了一个方法&#xff0c;方法中还调用了该方法&#xff0c;相当于自己调用自己&#xff0c;如果书写不当&#xff0c;就会有堆栈溢出的风险&#xff0c;无法跳出。 所以我们编写…

虚拟机centos9搭建wordpress

目录 1. 更换yum源更新系统软件包&#xff1a; 1.1备份yum源 1.1.1创建备份目录&#xff1a; 1.1.2移动现有仓库配置文件到备份目录&#xff1a; 1.1.3验证备份&#xff1a; 1.2更换yum源 1.2.1添加yum源 1.2.2删除和建立yum缓存 1.3更新系统软件包 1.4 yum与dnf介绍…

RV1126 Linux 系统,接外设,时好时坏(二)排查问题的常用命令

在 RV1126 Linux 系统中,排查外设连接问题时,可以使用多种命令来诊断和调试。以下是一些常用的命令和工具: 1. 查看系统日志 dmesg: 显示内核环形缓冲区的消息,通常包含设备初始化、驱动加载和错误等信息。 dmesg | grep <设备名或相关关键字>journalctl: 查看系统…

做短视频素材哪里找?去哪里下载?自媒体下载素材网站分享

自媒体视频创作&#xff1a;高质量素材网站大公开&#xff01; 大家好&#xff0c;我是一名热情的短视频创作者。今天&#xff0c;我要与大家分享一些寻找优质视频素材的秘诀。无论是新手还是老手&#xff0c;这些建议都能帮助你的视频在众多平台中脱颖而出&#xff0c;吸引更…

Java面试八股之Spring-boot-starter-parent的作用是什么

Spring-boot-starter-parent的作用是什么 spring-boot-starter-parent 是Spring Boot项目中的一个特殊POM&#xff08;Project Object Model&#xff09;&#xff0c;它主要的作用是提供一系列默认的配置和依赖管理&#xff0c;以便简化项目的构建过程。以下是spring-boot-sta…

二十、【机器学习】【非监督学习】- 均值漂移 (Mean Shift)

系列文章目录 第一章 【机器学习】初识机器学习 第二章 【机器学习】【监督学习】- 逻辑回归算法 (Logistic Regression) 第三章 【机器学习】【监督学习】- 支持向量机 (SVM) 第四章【机器学习】【监督学习】- K-近邻算法 (K-NN) 第五章【机器学习】【监督学习】- 决策树…

【支持语言模型和视觉语言模型的推理引擎sglang】

介绍 sglang是一个AI推理引擎&#xff0c;是一个专门为大语言模型和视觉语言模型设计的高效服务框架。 就像F1赛车需要顶级发动机一样&#xff0c;大语言模型也需要高效的推理引擎来发挥潜力。 而sglang正是这样一个性能怪兽。 根据LMSys组织的官方公告&#xff0c;最新的s…

Milvus × RAG助力快看多业务应用

快看介绍 快看漫画创办于2014年&#xff0c;集漫画阅读、创作互动、线下漫画沉浸体验、周边衍生品购买等体验于一体&#xff0c;是年轻人的一站式漫画生活方式平台。截止到2023年底&#xff0c;快看总用户超过3.8亿&#xff0c;在中国漫画市场渗透率超过50%。经过9年的创作者生…

Mybatis-plus自动生成MVC架构

系列文章目录 目录 系列文章目录 文章目录 前言 核心特性 一、mybatis-plus插件介绍 二、使用步骤 1.下载插件 2.读入数据 总结 前言 MyBatis-Plus&#xff08;简称 MP&#xff09;是一个基于 MyBatis 的增强工具包&#xff0c;旨在简化开发流程并提高开发效率。以下…

如何使用EXCEL访问WinCC中的实时数据实现报表

如果项目已经做好了&#xff0c;不想改动现有项目。那么可以使用 EXCEL 通过 OPC 方式访问 WinCC 项目的数据。预先定义好 EXCEL 表格样式&#xff0c;通过以下方式实现。通过以下步骤打开 EXCEL 中的 VB 编辑器 引用 WinCC 提供的 OPC 客户端 Control 控件: Siemens OPC DAAut…