C#實(shí)現(xiàn)跨進(jìn)程條件變量的示例代碼
前言
C#提供的多進(jìn)程同步對(duì)象有互斥鎖和信號(hào)量,但是并沒(méi)有條件變量。雖然信號(hào)量條件變量一定程度可以等效,但是具體的使用還是會(huì)有區(qū)別。比如說(shuō)消息隊(duì)列用條件變量就比信號(hào)量方便,用信號(hào)量要么缺乏靈活性,要么輔助代碼已經(jīng)和實(shí)現(xiàn)一個(gè)條件變量沒(méi)區(qū)別了。本文提供了一種條件變量的實(shí)現(xiàn)方法,可以用于進(jìn)程間的同步控制。
一、關(guān)鍵實(shí)現(xiàn)
1、用到的主要對(duì)象
下列對(duì)象都是跨進(jìn)程的
//互斥變量, Mutex _mtx; //等待發(fā)送信號(hào)量 Semaphore _waitSem; //等待完成信號(hào)量 Semaphore _waitDone; //共享內(nèi)存,用于存儲(chǔ)計(jì)數(shù)變量 MemoryMappedFile _mmf; //共享內(nèi)存的讀寫(xiě)對(duì)象 MemoryMappedViewAccessor _mmva;
2、初始化區(qū)分創(chuàng)建和打開(kāi)
利用Mutex判斷是創(chuàng)建還是打開(kāi)
bool isCreateNew;
_mtx = new Mutex(false, name, out isCreateNew);
if(isCreateNew){
//只能在創(chuàng)建時(shí),初始化共享變量
}
3、變量放到共享內(nèi)存
條件變量需要的計(jì)算對(duì)象就兩個(gè)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的條件變量實(shí)現(xiàn),具體略。有興趣的朋友可以自行查找源碼查看。
二、完整代碼
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;
namespace AC
{
/************************************************************************
* @Project: AC::ConditionVariable
* @Decription: 條件變量
* 支持跨進(jìn)程
* @Verision: v1.0.0
* 更新日志
* v1.0.0:實(shí)現(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>
/// 構(gòu)造方法
/// </summary>
public ConditionVariable()
{
bool isCreateNew;
Initialize(null, out isCreateNew);
}
/// <summary>
/// 構(gòu)造方法
/// </summary>
/// <param name="name">唯一名稱(chēng),系統(tǒng)級(jí)別,不同進(jìn)程創(chuàng)建相同名稱(chēng)的本對(duì)象,就是同一個(gè)條件變量。</param>
public ConditionVariable(string? name)
{
bool isCreateNew;
Initialize(name, out isCreateNew);
}
/// <summary>
/// 構(gòu)造方法
/// </summary>
/// <param name="name">唯一名稱(chēng),系統(tǒng)級(jí)別,不同進(jìn)程創(chuàng)建相同名稱(chēng)的本對(duì)象,就是同一個(gè)條件變量。</param>
/// <param name="isCreateNew">表示是否新創(chuàng)建,是則是創(chuàng)建,否則是打開(kāi)已存在的。</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>
/// 等待超時(shí)
/// </summary>
/// <param name="timeout">超時(shí)時(shí)間</param>
/// <param name="outerMtx">外部鎖</param>
/// <returns>是則成功,否則超時(shí)</returns>
public bool WaitOne(TimeSpan timeout, Mutex outerMtx)
{
bool isNotTimeout;
//記錄等待數(shù)量
_mtx.WaitOne();
var ws = Data;
ws.Waiting++;
Data = ws;
_mtx.ReleaseMutex();
//解除外部的互斥鎖,讓其他線(xiàn)程可以進(jìn)入條件等待。
outerMtx.ReleaseMutex();
//等待信號(hào)
isNotTimeout = _waitSem.WaitOne(timeout);
_mtx.WaitOne();
ws = Data;
if (isNotTimeout && ws.Signals > 0)
{
//通知發(fā)送信號(hào)的線(xiàn)程,等待完成。
_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>
/// 銷(xiāo)毀對(duì)象,只會(huì)銷(xiāo)毀當(dāng)前實(shí)例,如果多個(gè)打開(kāi)同個(gè)名稱(chēng),其他對(duì)象不受影響
/// </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 = "";
//子線(xiàn)程發(fā)送消息
new Thread(() =>
{
int n = 0;
while (true)
{
mutex.WaitOne();
text = (n++).ToString();
//通知主線(xiàn)程
cv.Release();
mutex.ReleaseMutex();
}
}).Start();
//主線(xiàn)程接收消息
while (true)
{
mutex.WaitOne();
//等待子消息
cv.WaitOne(mutex);
Console.WriteLine(text);
mutex.ReleaseMutex();
}

2、跨進(jìn)程控制
進(jìn)程A
//不同進(jìn)程名稱(chēng)相同就是同一個(gè)對(duì)象
ConditionVariable cv = new ConditionVariable("cv1");
Mutex mutex = new Mutex(false,"mx1");
//進(jìn)程A發(fā)送消息
while (true)
{
mutex.WaitOne();
//共享進(jìn)程讀寫(xiě)略
//通知進(jìn)程B
cv.Release();
mutex.ReleaseMutex();
}
進(jìn)程B
//不同進(jìn)程名稱(chēng)相同就是同一個(gè)對(duì)象
ConditionVariable cv = new ConditionVariable("cv1");
Mutex mutex = new Mutex(false,"mx1");
//進(jìn)程B接收消息
while (true)
{
mutex.WaitOne();
//等待進(jìn)A程消息
cv.WaitOne(mutex);
//共享進(jìn)程讀寫(xiě)略
Console.WriteLine("收到進(jìn)程A消息");
mutex.ReleaseMutex();
}

總結(jié)
以上就是今天要講的內(nèi)容,之所以實(shí)現(xiàn)這樣一個(gè)對(duì)象是因?yàn)?,筆者在寫(xiě)跨進(jìn)程隊(duì)列通信,用信號(hào)量實(shí)現(xiàn)發(fā)現(xiàn)有所局限,想要完善與重寫(xiě)一個(gè)條件變量差異不大,索性直接實(shí)現(xiàn)一個(gè)條件變量,提供給隊(duì)列使用,同時(shí)還具體通用性,在其他地方也能使用??偟膩?lái)說(shuō),條件變量還是有用的,雖然需要遇到相應(yīng)的使用場(chǎng)景才能意識(shí)到它的作用。
到此這篇關(guān)于C#實(shí)現(xiàn)跨進(jìn)程條件變量的示例代碼的文章就介紹到這了,更多相關(guān)C#跨進(jìn)程條件變量?jī)?nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
c#linq里的Skip和Take實(shí)現(xiàn)分頁(yè)或遍歷
LINQ的優(yōu)勢(shì)在于它提供了一種直觀(guān)、類(lèi)型安全的方式來(lái)操作各種類(lèi)型的數(shù)據(jù),查詢(xún)常需要獲取一部分?jǐn)?shù)據(jù),為了實(shí)現(xiàn)這一功能,LINQ提供了Take?和Skip運(yùn)算符,Take運(yùn)算符用于從一個(gè)序列中返回指定個(gè)數(shù)的元素,Skip運(yùn)算符用于從一個(gè)序列中跳過(guò)指定個(gè)數(shù)的元素2024-01-01
C#實(shí)現(xiàn)ComboBox控件顯示出多個(gè)數(shù)據(jù)源屬性的方法
這篇文章主要介紹了C#實(shí)現(xiàn)ComboBox控件顯示出多個(gè)數(shù)據(jù)源屬性的方法,實(shí)例分析了ComboBox控件的使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-09-09
如何使用LinQ To Object把數(shù)組或DataTable中的數(shù)據(jù)進(jìn)行向上匯總
這篇文章主要介紹了如何使用LinQ To Object把數(shù)組或DataTable中的數(shù)據(jù)進(jìn)行向上匯總,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12
C#設(shè)置軟件開(kāi)機(jī)自動(dòng)運(yùn)行的方法(修改注冊(cè)表)
這篇文章主要介紹了C#設(shè)置軟件開(kāi)機(jī)自動(dòng)運(yùn)行的方法,通過(guò)簡(jiǎn)單修改注冊(cè)表開(kāi)機(jī)啟動(dòng)項(xiàng)實(shí)現(xiàn)軟件的開(kāi)機(jī)啟動(dòng)功能,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下2016-06-06

