P2: Flexible四类通信锁从全局静态改为按端口/IP粒度(ConcurrentDictionary),不同物理端口互不阻塞

This commit is contained in:
2026-09-14 13:53:46 +08:00
parent 8d8df56544
commit 7043a66a28
4 changed files with 62 additions and 42 deletions
+17 -12
View File
@@ -1,6 +1,7 @@
using Common.Attributes;
using NModbus;
using System;
using System.Collections.Concurrent;
using System.Net.Sockets;
using System.Text;
using System.Threading;
@@ -10,14 +11,18 @@ namespace DeviceCommand.Flexible
{
/// <summary>
/// 灵活型 Modbus TCP 通信类(免实例化,每次调用临时建立 TCP 连接)。
/// 支持保持寄存器与线圈的读写操作,内部使用通信锁保证同一时刻只有一个事务在执行,
/// 适用于偶发性、无需保持长连接的 Modbus TCP 设备读写场景。
/// 支持保持寄存器与线圈的读写操作,内部使用按端点粒度的通信锁保证同一端点同一时刻只有一个事务在执行,
/// 不同端点之间互不阻塞,适用于偶发性、无需保持长连接的 Modbus TCP 设备读写场景。
/// </summary>
[ADPCommand]
public static class FModbusTCP
{
// 通信锁:保证同一时刻只有一个 Modbus 事务在执行
private static readonly SemaphoreSlim _commLock = new(1, 1);
// 按端点粒度的通信锁:同一端点同一时刻只有一个事务在执行,不同端点互不阻塞
private static readonly ConcurrentDictionary<string, SemaphoreSlim> _commLocks = new();
/// <summary>获取指定端点的通信锁(不存在则自动创建)</summary>
private static SemaphoreSlim GetLock(string ipAddress, int port)
=> _commLocks.GetOrAdd($"{ipAddress}:{port}", _ => new SemaphoreSlim(1, 1));
/// <summary>
/// 建立 TCP 连接并创建 Modbus TCP 主站。
@@ -57,7 +62,7 @@ namespace DeviceCommand.Flexible
int receiveTimeout = 3000,
CancellationToken ct = default)
{
await _commLock.WaitAsync(ct);
await GetLock(ipAddress, port).WaitAsync(ct);
try
{
using var master = await ConnectAsync(ipAddress, port, sendTimeout, receiveTimeout, ct) as IDisposable;
@@ -68,7 +73,7 @@ namespace DeviceCommand.Flexible
}
finally
{
_commLock.Release();
GetLock(ipAddress, port).Release();
}
}
@@ -93,7 +98,7 @@ namespace DeviceCommand.Flexible
int receiveTimeout = 3000,
CancellationToken ct = default)
{
await _commLock.WaitAsync(ct);
await GetLock(ipAddress, port).WaitAsync(ct);
try
{
using var master = await ConnectAsync(ipAddress, port, sendTimeout, receiveTimeout, ct) as IDisposable;
@@ -103,7 +108,7 @@ namespace DeviceCommand.Flexible
}
finally
{
_commLock.Release();
GetLock(ipAddress, port).Release();
}
}
@@ -133,7 +138,7 @@ namespace DeviceCommand.Flexible
int receiveTimeout = 3000,
CancellationToken ct = default)
{
await _commLock.WaitAsync(ct);
await GetLock(ipAddress, port).WaitAsync(ct);
try
{
using var master = await ConnectAsync(ipAddress, port, sendTimeout, receiveTimeout, ct) as IDisposable;
@@ -144,7 +149,7 @@ namespace DeviceCommand.Flexible
}
finally
{
_commLock.Release();
GetLock(ipAddress, port).Release();
}
}
@@ -169,7 +174,7 @@ namespace DeviceCommand.Flexible
int receiveTimeout = 3000,
CancellationToken ct = default)
{
await _commLock.WaitAsync(ct);
await GetLock(ipAddress, port).WaitAsync(ct);
try
{
using var master = await ConnectAsync(ipAddress, port, sendTimeout, receiveTimeout, ct) as IDisposable;
@@ -179,7 +184,7 @@ namespace DeviceCommand.Flexible
}
finally
{
_commLock.Release();
GetLock(ipAddress, port).Release();
}
}