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.xdnf.cn/news/1487853.html

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

相关文章

Maven概述

目录 1.Maven简介 2.Maven开发环境搭建 2.1下载Maven服务器 2.2安装&#xff0c;配置Maven 1.配置本地仓库地址 2.配置阿里云镜像地址 2.3在idea中配置maven 2.4在idea中创建maven项目 3.pom.xml配置 1.项目基本信息 2.依赖信息 3.构建信息 4.Maven命令 5.打包Jav…

【PyTorch】图像多分类项目

【PyTorch】图像二分类项目 【PyTorch】图像二分类项目-部署 【PyTorch】图像多分类项目 【PyTorch】图像多分类项目部署 多类图像分类的目标是为一组固定类别中的图像分配标签。 目录 加载和处理数据 搭建模型 定义损失函数 定义优化器 训练和迁移学习 用随机权重进行训…

【HTML — 构建网络】HTML 入门

在本文中,我们将介绍 HTML 的绝对基础知识。为了帮助您入门,本文定义了元素、属性以及您可能听说过的所有其他重要术语。它还解释了这些在 HTML 中的位置。您将学习 HTML 元素的结构、典型的 HTML 页面的结构以及其他重要的基本语言功能。在此过程中,也将有机会玩转 HTML! …

SpringBoot 项目配置文件注释乱码的问题解决方案

一、问题描述 在项目的配置文件中&#xff0c;我们写了一些注释&#xff0c;如下所示&#xff1a; 但是再次打开注释会变成乱码&#xff0c;如下所示&#xff1a; 那么如何解决呢&#xff1f; 二、解决方案 1. 点击” File→Setting" 2. 搜索“File Encodings”, 将框…

Mac安装Hoomebrew与升级Python版本

参考 mac 安装HomeBrew(100%成功)_mac安装homebrew-CSDN博客 /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)" 安装了Python 3.x版本&#xff0c;你可以使用以下命令来设置默认的Python版本&#xff1a; # 首先找到新安…

图片格式转换工具哪个好?一键转换图片格式就用这4个

在贵州的街头&#xff0c;福建的游神活动吸引了众多目光&#xff0c;人们纷纷拿出手机记录下这难得一见的盛况。然而&#xff0c;在分享这些精彩瞬间时&#xff0c;我们往往会遇到格式不兼容的问题。 想象一下&#xff0c;如果你能一键将手机拍摄的HEIC格式转换为更通用的JPG或…

2024 微信小程序 学习笔记 第二天

1. WXML 模板语法 数据绑定 事件绑定 条件渲染 列表渲染 2. WXSS 模板样式 rpx 样式导入 全局和局部样式 3. 全局配置 window tabBar 配置tabBar案例 4. 网络数据请求 Get请求 Post 请求 加载时请求 5. 案例 -本地生活&#xff08;首页&#xff09; 导航栏 轮播图 九宫格效果…

leetcode10 -- 正则表达式匹配

题目描述&#xff1a; 给你一个字符串 s 和一个字符规律 p&#xff0c;请你来实现一个支持 . 和 * 的正则表达式匹配。 . 匹配任意单个字符* 匹配零个或多个前面的那一个元素 所谓匹配&#xff0c;是要涵盖 整个 字符串 s的&#xff0c;而不是部分字符串。 示例 1&#xff1…

配置sublime的中的C++编译器(.sublime-build),实现C++20

GCC 4.8: 支持 C11 (部分) GCC 4.9: 支持 C11 和 C14 (部分) GCC 5: 完全支持 C14 GCC 6: 支持 C14 和 C17 (部分) GCC 7: 支持 C17 (大部分) GCC 8: 完全支持 C17&#xff0c;部分支持 C20 GCC 9: 支持更多的 C20 特性 GCC 10: 支持大部分 C20 特性 GCC 11: 更全面地支持 C20 …

原生PHP/JS自主开发的交友内核框架婚恋交友系统V10

本文来自&#xff1a;婚恋交友系统V10 - 源码1688 应用介绍 原生PHP/JS自主开发的交友内核框架&#xff0c;极高性能、无捆绑、自主权、无流水扣点、独立全开源 01脱单盲盒&#xff1a;脱单盲盒类似于漂流瓶&#xff0c;先将自己《投放》到盲盒中&#xff0c;另一伴有缘将您取…

解决显存不足问题:深度学习中的 Batch Size 调整【模型训练】

解决显存不足问题&#xff1a;深度学习中的 Batch Size 调整 在深度学习训练中&#xff0c;显存不足是一个常见的问题&#xff0c;特别是在笔记本等显存有限的设备上。本文将解释什么是 Batch Size&#xff0c;为什么调整 Batch Size 可以缓解显存不足的问题&#xff0c;以及调…

【Git多人协作开发】同一分支下的多人协作开发模式

目录 0.前言场景 1.开发者1☞完成准备工作&协作开发 1.1创建dev分支开发 1.2拉取远程dev分支至本地 1.3查看分支情况和分支联系情况 1.4创建本地dev分支且与远程dev分支建立联系 1.5在本地dev分支上开发file.txt 1.6推送push至远程仓库 2.开发者2☞完成准备工作&…

【学习笔记】Elasticsearch学习汇总(包含SpringData、Spark、Flink操作)

文章目录 前言数据类型种类ES解决什么问题ELK StackES是什么数据格式正排(正向)索引倒排索引创建索引索引查询索引删除创建文档(添加数据)自定义ID 简单查询类似于主键查询查询所有数据 修改数据全量修改局部修改 删除数据条件查询请求路径(不推荐)请求体全查询分页查询指定查询…

普元EOS学习笔记-EOS的ide开发工具的介绍

前言 普元EOS开发包括低开和高开。 EOS低开&#xff0c;直接在浏览器操作即可&#xff0c;不需要编码。 EOS高开&#xff0c;需要使用EOS的ide工具&#xff0c;进行编码开发。 EOS的ide工具是普元在Eclipse基础上进行的扩展&#xff0c;添加了若干插件&#xff0c;专门用于…

如何使用Proxy实现JavaScript中的观察者模式

在软件开发中&#xff0c;尤其是JavaScript中&#xff0c;观察者模式是一种行为设计模式&#xff0c;它定义了一种一对多的关系。它允许多个观察者对象监听一个主题对象&#xff0c;并在主题状态发生变化时自动得到通知。这种模式常用于事件系统、数据绑定等场景。 在JavaScrip…

【React】箭头函数:现代 JavaScript 的高效编程方式

文章目录 一、箭头函数的基本语法二、箭头函数的特性三、在 React 中的常见用法四、最佳实践 在现代 JavaScript 中&#xff0c;箭头函数&#xff08;Arrow Functions&#xff09;是一种简洁的函数表达方式&#xff0c;并且在 React 开发中非常常见。箭头函数不仅简化了函数的语…

HighConcurrencyCommFramework c++通讯服务器框架 :目录,修改标题,配置,日志打印

目录规划 nginx 根目录下的三个文件 makefile :编译项目的入口&#xff0c;编译项目从这里开始 config.mk&#xff1a;也是个配置脚本用来增加变动的东西&#xff0c;应付可变 common.mk&#xff1a;最核心的编译脚本&#xff0c;每个子目录都要被编译.cpp程序 配置文件 配…

构建未来客户服务的智能平台架构

随着科技的飞速发展&#xff0c;客户服务不再仅仅是响应投诉和提供支持&#xff0c;而是成为企业与客户之间重要的互动和沟通桥梁。在这个信息化和智能化的时代&#xff0c;构建一个优秀的客户服务平台架构至关重要&#xff0c;它不仅能提升服务效率&#xff0c;还能增强客户满…

《0基础》学习Python——第二十一讲__网络爬虫/<4>爬取豆瓣电影电影信息

爬取网页数据&#xff08;获取网页信息全过程&#xff09; 1、爬取豆瓣电影的电影名称、导演、主演、年份、国家、评价 2、首先我们先爬取页面然后再获取信息 1、爬取网页源码 import requests from lxml import etree if __name__ __main__:#UA伪装head{User-Agent:Mozilla/…

Bootstrap实现dialog上一步下一步多个弹窗交互

Bootstrap实现dialog上一步下一步多个弹窗交互 版本介绍&#xff1a; Bootstrap v3.3.7jQuery v3.5.1 一、功能介绍 重新设置bootstrap主题色内容区以card形式展示&#xff0c;纯js实现分页功能共两步骤&#xff0c;第一步选择模板&#xff0c;第二步进行其他操作步骤一内的按…