C#實現(xiàn)跨進程條件變量的示例代碼
前言
C#提供的多進程同步對象有互斥鎖和信號量,但是并沒有條件變量。雖然信號量條件變量一定程度可以等效,但是具體的使用還是會有區(qū)別。比如說消息隊列用條件變量就比信號量方便,用信號量要么缺乏靈活性,要么輔助代碼已經(jīng)和實現(xiàn)一個條件變量沒區(qū)別了。本文提供了一種條件變量的實現(xiàn)方法,可以用于進程間的同步控制。
一、關鍵實現(xiàn)
1、用到的主要對象
下列對象都是跨進程的
//互斥變量, Mutex _mtx; //等待發(fā)送信號量 Semaphore _waitSem; //等待完成信號量 Semaphore _waitDone; //共享內(nèi)存,用于存儲計數(shù)變量 MemoryMappedFile _mmf; //共享內(nèi)存的讀寫對象 MemoryMappedViewAccessor _mmva;
2、初始化區(qū)分創(chuàng)建和打開
利用Mutex判斷是創(chuàng)建還是打開
bool isCreateNew; _mtx = new Mutex(false, name, out isCreateNew); if(isCreateNew){ //只能在創(chuàng)建時,初始化共享變量 }
3、變量放到共享內(nèi)存
條件變量需要的計算對象就兩個Waiting、Signals表示等待數(shù)和釋放數(shù)。
//放到共享內(nèi)存的數(shù)據(jù) 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的條件變量實現(xiàn),具體略。有興趣的朋友可以自行查找源碼查看。
二、完整代碼
using System.IO.MemoryMappedFiles; using System.Runtime.InteropServices; namespace AC { /************************************************************************ * @Project: AC::ConditionVariable * @Decription: 條件變量 * 支持跨進程 * @Verision: v1.0.0 * 更新日志 * v1.0.0:實現(xiàn)基本功能 * @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">唯一名稱,系統(tǒng)級別,不同進程創(chuàng)建相同名稱的本對象,就是同一個條件變量。</param> public ConditionVariable(string? name) { bool isCreateNew; Initialize(name, out isCreateNew); } /// <summary> /// 構造方法 /// </summary> /// <param name="name">唯一名稱,系統(tǒng)級別,不同進程創(chuàng)建相同名稱的本對象,就是同一個條件變量。</param> /// <param name="isCreateNew">表示是否新創(chuàng)建,是則是創(chuàng)建,否則是打開已存在的。</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) { bool isNotTimeout; //記錄等待數(shù)量 _mtx.WaitOne(); var ws = Data; ws.Waiting++; Data = ws; _mtx.ReleaseMutex(); //解除外部的互斥鎖,讓其他線程可以進入條件等待。 outerMtx.ReleaseMutex(); //等待信號 isNotTimeout = _waitSem.WaitOne(timeout); _mtx.WaitOne(); ws = Data; if (isNotTimeout && ws.Signals > 0) { //通知發(fā)送信號的線程,等待完成。 _waitDone.Release(); ws.Signals--; } ws.Waiting--; Data = ws; _mtx.ReleaseMutex(); //加上外部互斥鎖,還原外部的鎖狀態(tài)。 outerMtx.WaitOne(); return !isNotTimeout; } /// <summary> /// 釋放,通知 /// </summary> public void Release() { _mtx.WaitOne(); var ws = Data; if (ws.Waiting > ws.Signals) { ws.Signals++; Data = ws; _waitSem.Release(); _mtx.ReleaseMutex(); _waitDone.WaitOne(); } else { _mtx.ReleaseMutex(); } } /// <summary> /// 釋放全部,廣播 /// </summary> public void ReleaseAll() { _mtx.WaitOne(); var ws = Data; if (ws.Waiting > ws.Signals) { int waiting = ws.Waiting - ws.Signals; ws.Signals = ws.Waiting; Data = ws; _waitSem.Release(waiting); _mtx.ReleaseMutex(); _waitDone.WaitOne(waiting); } else { _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 { 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(); } } catch { mtx?.Dispose(); waitSem?.Dispose(); waitDone?.Dispose(); mmf?.Dispose(); mmva?.Dispose(); isCreateNew = false; throw; } } Mutex _mtx; Semaphore _waitSem; Semaphore _waitDone; MemoryMappedFile _mmf; MemoryMappedViewAccessor _mmva; 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; } } static string _TempDirectory = Path.GetTempPath() + "EE3E9111-8F65-4D68-AB2B-A018DD9ECF3C"; } }
三、使用示例
1、同步控制
using AC; ConditionVariable cv = new ConditionVariable(); Mutex mutex = new Mutex(); string text = ""; //子線程發(fā)送消息 new Thread(() => { int n = 0; while (true) { mutex.WaitOne(); text = (n++).ToString(); //通知主線程 cv.Release(); mutex.ReleaseMutex(); } }).Start(); //主線程接收消息 while (true) { mutex.WaitOne(); //等待子消息 cv.WaitOne(mutex); Console.WriteLine(text); mutex.ReleaseMutex(); }
2、跨進程控制
進程A
//不同進程名稱相同就是同一個對象 ConditionVariable cv = new ConditionVariable("cv1"); Mutex mutex = new Mutex(false,"mx1"); //進程A發(fā)送消息 while (true) { mutex.WaitOne(); //共享進程讀寫略 //通知進程B cv.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(); }
總結
以上就是今天要講的內(nèi)容,之所以實現(xiàn)這樣一個對象是因為,筆者在寫跨進程隊列通信,用信號量實現(xiàn)發(fā)現(xiàn)有所局限,想要完善與重寫一個條件變量差異不大,索性直接實現(xiàn)一個條件變量,提供給隊列使用,同時還具體通用性,在其他地方也能使用。總的來說,條件變量還是有用的,雖然需要遇到相應的使用場景才能意識到它的作用。
到此這篇關于C#實現(xiàn)跨進程條件變量的示例代碼的文章就介紹到這了,更多相關C#跨進程條件變量內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
c#linq里的Skip和Take實現(xiàn)分頁或遍歷
LINQ的優(yōu)勢在于它提供了一種直觀、類型安全的方式來操作各種類型的數(shù)據(jù),查詢常需要獲取一部分數(shù)據(jù),為了實現(xiàn)這一功能,LINQ提供了Take?和Skip運算符,Take運算符用于從一個序列中返回指定個數(shù)的元素,Skip運算符用于從一個序列中跳過指定個數(shù)的元素2024-01-01C#實現(xiàn)ComboBox控件顯示出多個數(shù)據(jù)源屬性的方法
這篇文章主要介紹了C#實現(xiàn)ComboBox控件顯示出多個數(shù)據(jù)源屬性的方法,實例分析了ComboBox控件的使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-09-09如何使用LinQ To Object把數(shù)組或DataTable中的數(shù)據(jù)進行向上匯總
這篇文章主要介紹了如何使用LinQ To Object把數(shù)組或DataTable中的數(shù)據(jù)進行向上匯總,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-12-12