From 0b829ac85c174bd896b1135bd17dd37b6c63ff7d Mon Sep 17 00:00:00 2001 From: "2248356998 qq.com" <2248356998@qq.com> Date: Mon, 8 Sep 2025 21:16:37 +0800 Subject: [PATCH] 10.11.33 --- .gitignore | 3 - .../Collections/ObjectPool.cs | 54 +---- src/Directory.Build.props | 6 +- .../Channel/OtherChannel.cs | 29 +-- .../Channel/SerialPortChannel.cs | 18 +- .../Channel/TcpClientChannel.cs | 15 +- .../Channel/TcpSessionClientChannel.cs | 17 +- .../Channel/UdpSessionChannel.cs | 18 +- .../Device/DeviceBase.cs | 48 ++--- .../LinkedCancellationTokenSourceCache.cs | 21 +- .../Utils/ReusableCancellationTokenSource.cs | 66 ++---- .../Benchmark/ModbusBenchmark.cs | 194 ++++++++++++++++++ .../Benchmark/S7Benchmark.cs | 153 ++++++++++++++ .../Benchmark/TimeoutBenchmark.cs | 62 ++++++ .../Program.cs | 59 ++++++ .../ThingsGateway.Foundation.Benchmark.csproj | 58 ++++++ 16 files changed, 630 insertions(+), 191 deletions(-) rename src/{Gateway/ThingsGateway.Gateway.Application/Common => Foundation/ThingsGateway.Foundation/Utils}/LinkedCancellationTokenSourceCache.cs (77%) create mode 100644 src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/ModbusBenchmark.cs create mode 100644 src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/S7Benchmark.cs create mode 100644 src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/TimeoutBenchmark.cs create mode 100644 src/Plugin/ThingsGateway.Foundation.Benchmark/Program.cs create mode 100644 src/Plugin/ThingsGateway.Foundation.Benchmark/ThingsGateway.Foundation.Benchmark.csproj diff --git a/.gitignore b/.gitignore index c733d2eea..590bcb57a 100644 --- a/.gitignore +++ b/.gitignore @@ -364,8 +364,5 @@ FodyWeavers.xsd /src/*Pro*/ /src/*Pro* -/src/**/*Pro* -/src/*pro* -/src/*pro*/ /src/ThingsGateway.Server/Configuration/GiteeOAuthSettings.json /src/.idea/ diff --git a/src/Admin/ThingsGateway.NewLife.X/Collections/ObjectPool.cs b/src/Admin/ThingsGateway.NewLife.X/Collections/ObjectPool.cs index 44e197592..257685363 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Collections/ObjectPool.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Collections/ObjectPool.cs @@ -1,5 +1,4 @@ using System.Collections.Concurrent; -using System.Diagnostics; using ThingsGateway.NewLife.Log; using ThingsGateway.NewLife.Reflection; @@ -76,7 +75,7 @@ public class ObjectPool : DisposeBase, IPool where T : notnull _timer.TryDispose(); - WriteLog($"Dispose {typeof(T).FullName} FreeCount={FreeCount:n0} BusyCount={BusyCount:n0} Total={Total:n0}"); + WriteLog($"Dispose {typeof(T).FullName} FreeCount={FreeCount:n0} BusyCount={BusyCount:n0}"); Clear(); } @@ -112,10 +111,6 @@ public class ObjectPool : DisposeBase, IPool where T : notnull /// public virtual T Get() { - var sw = Log == null || Log == Logger.Null ? null : Stopwatch.StartNew(); - Interlocked.Increment(ref _Total); - - var success = false; Item? pi = null; do { @@ -123,8 +118,6 @@ public class ObjectPool : DisposeBase, IPool where T : notnull if (_free.TryPop(out pi) || _free2.TryDequeue(out pi)) { Interlocked.Decrement(ref _FreeCount); - - success = true; } else { @@ -150,8 +143,6 @@ public class ObjectPool : DisposeBase, IPool where T : notnull WriteLog("Acquire Create Free={0} Busy={1}", FreeCount, count + 1); #endif - Interlocked.Increment(ref _NewCount); - success = false; } // 借出时如果不可用,再次借取 @@ -164,17 +155,6 @@ public class ObjectPool : DisposeBase, IPool where T : notnull _busy.TryAdd(pi.Value, pi); Interlocked.Increment(ref _BusyCount); - if (success) Interlocked.Increment(ref _Success); - if (sw != null) - { - sw.Stop(); - var ms = sw.Elapsed.TotalMilliseconds; - - if (Cost < 0.001) - Cost = ms; - else - Cost = (Cost * 3 + ms) / 4; - } return pi.Value; } @@ -200,7 +180,6 @@ public class ObjectPool : DisposeBase, IPool where T : notnull #if DEBUG WriteLog("Return Error"); #endif - Interlocked.Increment(ref _ReleaseCount); return false; } @@ -210,13 +189,11 @@ public class ObjectPool : DisposeBase, IPool where T : notnull // 是否可用 if (!OnReturn(value)) { - Interlocked.Increment(ref _ReleaseCount); return false; } if (value is DisposeBase db && db.Disposed) { - Interlocked.Increment(ref _ReleaseCount); return false; } @@ -373,39 +350,14 @@ public class ObjectPool : DisposeBase, IPool where T : notnull } } - var ncount = _NewCount; - var fcount = _ReleaseCount; - if (count > 0 || ncount > 0 || fcount > 0) + if (count > 0) { - Interlocked.Add(ref _NewCount, -ncount); - Interlocked.Add(ref _ReleaseCount, -fcount); - var p = Total == 0 ? 0 : (Double)Success / Total; - - WriteLog("Release New={6:n0} Release={7:n0} Free={0} Busy={1} 清除过期资源 {2:n0} 项。总请求 {3:n0} 次,命中 {4:p2},平均 {5:n2}us", FreeCount, BusyCount, count, Total, p, Cost * 1000, ncount, fcount); + WriteLog("Release New={6:n0} Release={7:n0} Free={0} Busy={1} 清除过期资源 {2:n0} 项。", FreeCount, BusyCount, count); } } #endregion - #region 统计 - private Int32 _Total; - /// 总请求数 - public Int32 Total => _Total; - - private Int32 _Success; - /// 成功数 - public Int32 Success => _Success; - - /// 新创建数 - private Int32 _NewCount; - - /// 释放数 - private Int32 _ReleaseCount; - - /// 平均耗时。单位ms - private Double Cost; - #endregion - #region 日志 /// 日志 public ILog Log { get; set; } = Logger.Null; diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 89bdf1dc1..4dd98e9b1 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,9 +1,9 @@ - 10.11.31 - 10.11.31 - 10.11.31 + 10.11.33 + 10.11.33 + 10.11.33 10.11.3 10.11.3 8.0.19 diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/OtherChannel.cs b/src/Foundation/ThingsGateway.Foundation/Channel/OtherChannel.cs index 1d6ce5a9a..160dc3c12 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/OtherChannel.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/OtherChannel.cs @@ -29,17 +29,7 @@ public class OtherChannel : SetupConfigObject, IClientChannel } public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config; - public void SetDataHandlingAdapterLogger(ILog log) - { - if (_deviceDataHandleAdapter != ReadOnlyDataHandlingAdapter && ReadOnlyDataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter) - { - _deviceDataHandleAdapter = handleAdapter; - } - if (_deviceDataHandleAdapter != null) - { - _deviceDataHandleAdapter.Logger = log; - } - } + public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue) { @@ -83,16 +73,27 @@ public class OtherChannel : SetupConfigObject, IClientChannel //private readonly WaitLock _connectLock = new WaitLock(); - private IDeviceDataHandleAdapter _deviceDataHandleAdapter; + + private bool logSet; + /// + public void SetDataHandlingAdapterLogger(ILog log) + { + if (!logSet && ReadOnlyDataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter) + { + logSet = true; + handleAdapter.Logger = log; + } + } /// public void SetDataHandlingAdapter(DataHandlingAdapter adapter) { if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter) SetAdapter(singleStreamDataHandlingAdapter); - if (adapter is IDeviceDataHandleAdapter deviceDataHandleAdapter) - _deviceDataHandleAdapter = deviceDataHandleAdapter; + + logSet = false; } + /// /// 设置数据处理适配器。 /// diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/SerialPortChannel.cs b/src/Foundation/ThingsGateway.Foundation/Channel/SerialPortChannel.cs index a0e419322..334b22b5f 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/SerialPortChannel.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/SerialPortChannel.cs @@ -47,16 +47,15 @@ public class SerialPortChannel : SerialPortClient, IClientChannel /// public DataHandlingAdapter ReadOnlyDataHandlingAdapter => ProtectedDataHandlingAdapter; - private IDeviceDataHandleAdapter _deviceDataHandleAdapter; + + private bool logSet; + /// public void SetDataHandlingAdapterLogger(ILog log) { - if (_deviceDataHandleAdapter != ProtectedDataHandlingAdapter && ProtectedDataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter) + if (!logSet && ProtectedDataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter) { - _deviceDataHandleAdapter = handleAdapter; - } - if (_deviceDataHandleAdapter != null) - { - _deviceDataHandleAdapter.Logger = log; + logSet = true; + handleAdapter.Logger = log; } } /// @@ -64,10 +63,11 @@ public class SerialPortChannel : SerialPortClient, IClientChannel { if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter) SetAdapter(singleStreamDataHandlingAdapter); - if (adapter is IDeviceDataHandleAdapter deviceDataHandleAdapter) - _deviceDataHandleAdapter = deviceDataHandleAdapter; + + logSet = false; } + /// public ChannelEventHandler Started { get; } = new(); diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/TcpClientChannel.cs b/src/Foundation/ThingsGateway.Foundation/Channel/TcpClientChannel.cs index e164f51af..0c277db14 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/TcpClientChannel.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/TcpClientChannel.cs @@ -30,16 +30,13 @@ public class TcpClientChannel : TcpClient, IClientChannel WaitHandlePool = new WaitHandlePool(minSign, maxSign); pool?.CancelAll(); } - private IDeviceDataHandleAdapter _deviceDataHandleAdapter; + private bool logSet; public void SetDataHandlingAdapterLogger(ILog log) { - if (_deviceDataHandleAdapter != DataHandlingAdapter && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter) + if (!logSet && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter) { - _deviceDataHandleAdapter = handleAdapter; - } - if (_deviceDataHandleAdapter != null) - { - _deviceDataHandleAdapter.Logger = log; + logSet = true; + handleAdapter.Logger = log; } } /// @@ -47,8 +44,8 @@ public class TcpClientChannel : TcpClient, IClientChannel { if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter) SetAdapter(singleStreamDataHandlingAdapter); - if (adapter is IDeviceDataHandleAdapter deviceDataHandleAdapter) - _deviceDataHandleAdapter = deviceDataHandleAdapter; + + logSet = false; } /// public ChannelReceivedEventHandler ChannelReceived { get; } = new(); diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/TcpSessionClientChannel.cs b/src/Foundation/ThingsGateway.Foundation/Channel/TcpSessionClientChannel.cs index 1053c1905..65351d6a6 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/TcpSessionClientChannel.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/TcpSessionClientChannel.cs @@ -21,16 +21,14 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel public TcpSessionClientChannel() { } - private IDeviceDataHandleAdapter _deviceDataHandleAdapter; + private bool logSet; + /// public void SetDataHandlingAdapterLogger(ILog log) { - if (_deviceDataHandleAdapter != DataHandlingAdapter && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter) + if (!logSet && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter) { - _deviceDataHandleAdapter = handleAdapter; - } - if (_deviceDataHandleAdapter != null) - { - _deviceDataHandleAdapter.Logger = log; + logSet = true; + handleAdapter.Logger = log; } } /// @@ -38,9 +36,10 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel { if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter) SetAdapter(singleStreamDataHandlingAdapter); - if (adapter is IDeviceDataHandleAdapter deviceDataHandleAdapter) - _deviceDataHandleAdapter = deviceDataHandleAdapter; + + logSet = false; } + public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue) { var pool = WaitHandlePool; diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/UdpSessionChannel.cs b/src/Foundation/ThingsGateway.Foundation/Channel/UdpSessionChannel.cs index 6f93143f3..db40b84b2 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/UdpSessionChannel.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/UdpSessionChannel.cs @@ -26,16 +26,14 @@ public class UdpSessionChannel : UdpSession, IClientChannel ResetSign(); } public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config; - private IDeviceDataHandleAdapter _deviceDataHandleAdapter; + private bool logSet; + /// public void SetDataHandlingAdapterLogger(ILog log) { - if (_deviceDataHandleAdapter != DataHandlingAdapter && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter) + if (!logSet && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter) { - _deviceDataHandleAdapter = handleAdapter; - } - if (_deviceDataHandleAdapter != null) - { - _deviceDataHandleAdapter.Logger = log; + logSet = true; + handleAdapter.Logger = log; } } /// @@ -43,9 +41,11 @@ public class UdpSessionChannel : UdpSession, IClientChannel { if (adapter is UdpDataHandlingAdapter udpDataHandlingAdapter) SetAdapter(udpDataHandlingAdapter); - if (adapter is IDeviceDataHandleAdapter deviceDataHandleAdapter) - _deviceDataHandleAdapter = deviceDataHandleAdapter; + + logSet = false; } + + public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue) { var pool = WaitHandlePool; diff --git a/src/Foundation/ThingsGateway.Foundation/Device/DeviceBase.cs b/src/Foundation/ThingsGateway.Foundation/Device/DeviceBase.cs index e5111ec54..f013a7ac5 100644 --- a/src/Foundation/ThingsGateway.Foundation/Device/DeviceBase.cs +++ b/src/Foundation/ThingsGateway.Foundation/Device/DeviceBase.cs @@ -331,34 +331,23 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice } public bool AutoConnect { get; protected set; } = true; /// - private async ValueTask SendAsync(ISendMessage sendMessage, IClientChannel channel, CancellationToken token = default) + private async Task SendAsync(ISendMessage sendMessage, IClientChannel channel, CancellationToken token = default) { - try + + if (SendDelayTime != 0) + await Task.Delay(SendDelayTime, token).ConfigureAwait(false); + + if (channel is IDtuUdpSessionChannel udpSession) { + EndPoint? endPoint = GetUdpEndpoint(); + await udpSession.SendAsync(endPoint, sendMessage, token).ConfigureAwait(false); - if (SendDelayTime != 0) - await Task.Delay(SendDelayTime, token).ConfigureAwait(false); - - if (token.IsCancellationRequested) - return new OperResult(new OperationCanceledException()); - - if (channel is IDtuUdpSessionChannel udpSession) - { - EndPoint? endPoint = GetUdpEndpoint(); - await udpSession.SendAsync(endPoint, sendMessage, token).ConfigureAwait(false); - - } - else - { - await channel.SendAsync(sendMessage, token).ConfigureAwait(false); - } - - return OperResult.Success; } - catch (Exception ex) + else { - return new(ex); + await channel.SendAsync(sendMessage, token).ConfigureAwait(false); } + } private Task BeforeSendAsync(IClientChannel channel, CancellationToken token) @@ -417,7 +406,8 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice channelResult.Content.SetDataHandlingAdapterLogger(Logger); - return await SendAsync(sendMessage, channelResult.Content, cancellationToken).ConfigureAwait(false); + await SendAsync(sendMessage, channelResult.Content, cancellationToken).ConfigureAwait(false); + return OperResult.Success; } finally { @@ -426,8 +416,6 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice } catch (Exception ex) { - if (!cancellationToken.IsCancellationRequested) - await Task.Delay(1000, cancellationToken).ConfigureAwait(false); return new(ex); } } @@ -538,7 +526,6 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice } private ObjectPool _reusableTimeouts = new(); - /// /// 发送并等待数据 /// @@ -548,6 +535,8 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice int timeout = 3000, CancellationToken cancellationToken = default) { + + var waitData = clientChannel.WaitHandlePool.GetWaitDataAsync(out var sign); command.Sign = sign; WaitLock? waitLock = null; @@ -557,13 +546,12 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice await BeforeSendAsync(clientChannel, cancellationToken).ConfigureAwait(false); waitLock = GetWaitLock(clientChannel); + await waitLock.WaitAsync(cancellationToken).ConfigureAwait(false); clientChannel.SetDataHandlingAdapterLogger(Logger); - var sendResult = await SendAsync(command, clientChannel, cancellationToken).ConfigureAwait(false); - if (!sendResult.IsSuccess) - return new MessageBase(sendResult); + await SendAsync(command, clientChannel, cancellationToken).ConfigureAwait(false); if (waitData.Status == WaitDataStatus.Success) return waitData.CompletedData; @@ -573,6 +561,7 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice var reusableTimeout = _reusableTimeouts.Get(); try { + var cts = reusableTimeout.GetTokenSource(timeout, cancellationToken, Channel.ClosedToken); await waitData.WaitAsync(cts.Token).ConfigureAwait(false); } @@ -606,6 +595,7 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice { waitLock?.Release(); waitData?.SafeDispose(); + } } diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Common/LinkedCancellationTokenSourceCache.cs b/src/Foundation/ThingsGateway.Foundation/Utils/LinkedCancellationTokenSourceCache.cs similarity index 77% rename from src/Gateway/ThingsGateway.Gateway.Application/Common/LinkedCancellationTokenSourceCache.cs rename to src/Foundation/ThingsGateway.Foundation/Utils/LinkedCancellationTokenSourceCache.cs index 403d58b40..803edb095 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Common/LinkedCancellationTokenSourceCache.cs +++ b/src/Foundation/ThingsGateway.Foundation/Utils/LinkedCancellationTokenSourceCache.cs @@ -8,35 +8,48 @@ // QQ群:605534569 //------------------------------------------------------------------------------ -namespace ThingsGateway.Gateway.Application; +namespace ThingsGateway.Foundation; public class LinkedCancellationTokenSourceCache : IDisposable { private CancellationTokenSource? _cachedCts; private CancellationToken _token1; private CancellationToken _token2; + private CancellationToken _token3; private readonly object _lock = new(); ~LinkedCancellationTokenSourceCache() { Dispose(); } + /// /// 获取一个 CancellationTokenSource,它是由两个 token 链接而成的。 /// 会尝试复用之前缓存的 CTS,前提是两个 token 仍然相同且未取消。 /// - public CancellationTokenSource GetLinkedTokenSource(CancellationToken token1, CancellationToken token2) + public CancellationTokenSource GetLinkedTokenSource(CancellationToken token1, CancellationToken token2, CancellationToken token3 = default) { lock (_lock) { // 如果缓存的 CTS 已经取消或 Dispose,或者 token 不同,重新创建 if (_cachedCts?.IsCancellationRequested != false || - !_token1.Equals(token1) || !_token2.Equals(token2)) + !_token1.Equals(token1) || !_token2.Equals(token2) || !_token3.Equals(token3)) { +#if NET6_0_OR_GREATER + if (_cachedCts?.TryReset() != true) + { + _cachedCts?.Dispose(); + _cachedCts = CancellationTokenSource.CreateLinkedTokenSource(token1, token2, token3); + } +#else _cachedCts?.Dispose(); - _cachedCts = CancellationTokenSource.CreateLinkedTokenSource(token1, token2); + _cachedCts = CancellationTokenSource.CreateLinkedTokenSource(token1, token2, token3); +#endif + + _token1 = token1; _token2 = token2; + _token3 = token3; } return _cachedCts; diff --git a/src/Foundation/ThingsGateway.Foundation/Utils/ReusableCancellationTokenSource.cs b/src/Foundation/ThingsGateway.Foundation/Utils/ReusableCancellationTokenSource.cs index fe2e4c0c3..8715275eb 100644 --- a/src/Foundation/ThingsGateway.Foundation/Utils/ReusableCancellationTokenSource.cs +++ b/src/Foundation/ThingsGateway.Foundation/Utils/ReusableCancellationTokenSource.cs @@ -18,7 +18,6 @@ using System.Threading; public sealed class ReusableCancellationTokenSource : IDisposable { private readonly Timer _timer; - private readonly object _lock = new(); private CancellationTokenSource? _cts; public ReusableCancellationTokenSource() @@ -30,59 +29,29 @@ public sealed class ReusableCancellationTokenSource : IDisposable private void OnTimeout(object? state) { - lock (_lock) - { - TimeoutStatus = true; + TimeoutStatus = true; - if (_cts?.IsCancellationRequested == false) - _cts?.Cancel(); + if (_cts?.IsCancellationRequested == false) + _cts?.Cancel(); - } } - /// - /// 获取一个 CTS,并启动超时 - /// - public CancellationTokenSource GetTokenSource(long timeout, CancellationToken external1 = default) - { - lock (_lock) - { - TimeoutStatus = false; - // 如果已有 CTS,先 Dispose - _cts?.SafeCancel(); - _cts?.SafeDispose(); - - // 创建新的 CTS - _cts = CancellationTokenSource.CreateLinkedTokenSource(external1); - - // 启动 Timer - _timer.Change(timeout, Timeout.Infinite); - - return _cts; - } - } + private readonly LinkedCancellationTokenSourceCache _linkedCtsCache = new(); /// /// 获取一个 CTS,并启动超时 /// public CancellationTokenSource GetTokenSource(long timeout, CancellationToken external1 = default, CancellationToken external2 = default, CancellationToken external3 = default) { - lock (_lock) - { - TimeoutStatus = false; + TimeoutStatus = false; - // 如果已有 CTS,先 Dispose - _cts?.SafeCancel(); - _cts?.SafeDispose(); + // 创建新的 CTS + _cts = _linkedCtsCache.GetLinkedTokenSource(external1, external2, external3); - // 创建新的 CTS - _cts = CancellationTokenSource.CreateLinkedTokenSource(external1, external2, external3); + // 启动 Timer + _timer.Change(timeout, Timeout.Infinite); - // 启动 Timer - _timer.Change(timeout, Timeout.Infinite); - - return _cts; - } + return _cts; } @@ -96,20 +65,15 @@ public sealed class ReusableCancellationTokenSource : IDisposable /// public void Cancel() { - lock (_lock) - { - _cts?.SafeCancel(); - } + _cts?.SafeCancel(); } public void Dispose() { - lock (_lock) - { - _cts?.SafeCancel(); - _cts?.SafeDispose(); - _timer.SafeDispose(); - } + _cts?.SafeCancel(); + _cts?.SafeDispose(); + _linkedCtsCache.SafeDispose(); + _timer.SafeDispose(); } } diff --git a/src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/ModbusBenchmark.cs b/src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/ModbusBenchmark.cs new file mode 100644 index 000000000..cb92b97db --- /dev/null +++ b/src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/ModbusBenchmark.cs @@ -0,0 +1,194 @@ +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://kimdiego2098.github.io/ +// QQ群:605534569 +//------------------------------------------------------------------------------ + +using BenchmarkConsoleApp; + +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Diagnosers; + +using HslCommunication.ModBus; + +using System.Net.Sockets; + +using ThingsGateway.Foundation.Modbus; + +using TouchSocket.Core; +using TouchSocket.Modbus; + +using IModbusMaster = NModbus.IModbusMaster; +using ModbusMaster = ThingsGateway.Foundation.Modbus.ModbusMaster; + +namespace ThingsGateway.Foundation; + +[MemoryDiagnoser] +public class ModbusBenchmark : IDisposable +{ + private List thingsgatewaymodbuss = new(); + private List nmodbuss = new(); + private List modbusTcpNets = new(); + private List modbusTcpMasters = new(); + + public ModbusBenchmark() + { + for (int i = 0; i < Program.ClientCount; i++) + { + + var clientConfig = new TouchSocket.Core.TouchSocketConfig(); + var clientChannel = clientConfig.GetTcpClientWithIPHost(new ChannelOptions() { RemoteUrl = "127.0.0.1:502", MaxConcurrentCount = 10 }); + var thingsgatewaymodbus = new ModbusMaster() + { + //modbus协议格式 + ModbusType = ModbusTypeEnum.ModbusTcp, + }; + thingsgatewaymodbus.InitChannel(clientChannel); + clientChannel.SetupAsync(clientChannel.Config).GetFalseAwaitResult(); + clientChannel.Logger.LogLevel = LogLevel.Warning; + thingsgatewaymodbus.ConnectAsync(CancellationToken.None).GetFalseAwaitResult(); + thingsgatewaymodbus.ReadAsync("40001", 100).GetAwaiter().GetResult(); + thingsgatewaymodbuss.Add(thingsgatewaymodbus); + } + + + for (int i = 0; i < Program.ClientCount; i++) + { + + var factory = new NModbus.ModbusFactory(); + var nmodbus = factory.CreateMaster(new TcpClient("127.0.0.1", 502)); + nmodbus.ReadHoldingRegistersAsync(1, 0, 100).GetFalseAwaitResult(); + nmodbuss.Add(nmodbus); + } + for (int i = 0; i < Program.ClientCount; i++) + { + ModbusTcpNet modbusTcpNet = new(); + modbusTcpNet.IpAddress = "127.0.0.1"; + modbusTcpNet.Port = 502; + modbusTcpNet.ConnectServer(); + modbusTcpNet.ReadAsync("0", 100).GetFalseAwaitResult(); + modbusTcpNets.Add(modbusTcpNet); + } + + for (int i = 0; i < Program.ClientCount; i++) + { + var client = new ModbusTcpMaster(); + client.SetupAsync(new TouchSocketConfig() + .SetRemoteIPHost("127.0.0.1:502")).GetFalseAwaitResult(); + client.ConnectAsync(CancellationToken.None).GetFalseAwaitResult(); + client.ReadHoldingRegistersAsync(0, 100).GetFalseAwaitResult(); + modbusTcpMasters.Add(client); + } + } + + [Benchmark] + public async Task ThingsGateway() + { + ModbusAddress addr = new ModbusAddress() { FunctionCode = 3, StartAddress = 0, Length = 100 }; + List tasks = new List(); + foreach (var thingsgatewaymodbus in thingsgatewaymodbuss) + { + + for (int i = 0; i < Program.TaskNumberOfItems; i++) + { + tasks.Add(Task.Run(async () => + { + for (int i = 0; i < Program.NumberOfItems; i++) + { + var result = await thingsgatewaymodbus.ModbusReadAsync(addr); + if (!result.IsSuccess) + { + throw new Exception(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff") + result.ToString()); + } + } + })); + } + } + await Task.WhenAll(tasks); + } + + + + [Benchmark] + public async Task TouchSocket() + { + List tasks = new List(); + foreach (var modbusTcpMaster in modbusTcpMasters) + { + for (int i = 0; i < Program.TaskNumberOfItems; i++) + { + tasks.Add(Task.Run(async () => + { + for (int i = 0; i < Program.NumberOfItems; i++) + { + var result = await modbusTcpMaster.ReadHoldingRegistersAsync(0, 100); + if (!result.IsSuccess) + { + throw new Exception(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff") + result.ToString()); + } + } + })); + } + } + await Task.WhenAll(tasks); + } + + + [Benchmark] + public async Task NModbus4() + { + List tasks = new List(); + foreach (var nmodbus in nmodbuss) + { + for (int i = 0; i < Program.TaskNumberOfItems; i++) + { + tasks.Add(Task.Run(async () => + { + for (int i = 0; i < Program.NumberOfItems; i++) + { + var result = await nmodbus.ReadHoldingRegistersAsync(1, 0, 100); + } + })); + } + } + await Task.WhenAll(tasks); + } + + + //并发失败 + //[Benchmark] + //public async Task HslCommunication() + //{ + // List tasks = new List(); + // foreach (var modbusTcpNet in modbusTcpNets) + // { + // for (int i = 0; i < Program.TaskNumberOfItems; i++) + // { + // tasks.Add(Task.Run(async () => + // { + // for (int i = 0; i < Program.NumberOfItems; i++) + // { + // var result = await modbusTcpNet.ReadAsync("0", 100); + // if (!result.IsSuccess) + // { + // throw new Exception(result.Message); + // } + // } + // })); + // } + // } + // await Task.WhenAll(tasks); + //} + + public void Dispose() + { + thingsgatewaymodbuss?.ForEach(a => a.Channel.SafeDispose()); + thingsgatewaymodbuss?.ForEach(a => a.SafeDispose()); + nmodbuss?.ForEach(a => a.SafeDispose()); + modbusTcpNets?.ForEach(a => a.SafeDispose()); + } +} \ No newline at end of file diff --git a/src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/S7Benchmark.cs b/src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/S7Benchmark.cs new file mode 100644 index 000000000..3f58a2ca2 --- /dev/null +++ b/src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/S7Benchmark.cs @@ -0,0 +1,153 @@ +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://kimdiego2098.github.io/ +// QQ群:605534569 +//------------------------------------------------------------------------------ + +using BenchmarkConsoleApp; + +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Diagnosers; + +using HslCommunication.Profinet.Siemens; + +using S7.Net; + +using ThingsGateway.Foundation.SiemensS7; + +using TouchSocket.Core; + +namespace ThingsGateway.Foundation; + +[MemoryDiagnoser] +public class S7Benchmark : IDisposable +{ + private List siemensS7s = new(); + + private List plcs = new(); + private List siemensS7Nets = new(); + + public S7Benchmark() + + { + { + for (int i = 0; i < Program.ClientCount; i++) + { + var clientConfig = new TouchSocket.Core.TouchSocketConfig(); + var clientChannel = clientConfig.GetTcpClientWithIPHost(new ChannelOptions() { RemoteUrl = "127.0.0.1:102" }); + var siemensS7 = new SiemensS7Master() + { + //modbus协议格式 + SiemensS7Type = SiemensTypeEnum.S1500 + }; + siemensS7.InitChannel(clientChannel); + clientChannel.SetupAsync(clientChannel.Config).GetFalseAwaitResult(); + clientChannel.Logger.LogLevel = LogLevel.Warning; + siemensS7.ConnectAsync(CancellationToken.None).GetFalseAwaitResult(); + siemensS7.ReadAsync("M1", 100).GetAwaiter().GetResult(); + siemensS7s.Add(siemensS7); + } + for (int i = 0; i < Program.ClientCount; i++) + { + var siemensS7Net = new SiemensS7Net(SiemensPLCS.S1500, "127.0.0.1"); + siemensS7Net.ConnectServer(); + siemensS7Net.ReadAsync("M0", 100).GetFalseAwaitResult(); + siemensS7Nets.Add(siemensS7Net); + } + for (int i = 0; i < Program.ClientCount; i++) + { + var plc = new Plc(CpuType.S7300, "127.0.0.1", 102, 0, 0); + plc.Open();//打开plc连接 + plc.ReadAsync(DataType.Memory, 1, 0, VarType.Byte, 100).GetFalseAwaitResult(); + plcs.Add(plc); + } + } + } + + [Benchmark] + public async Task S7netplus() + { + List tasks = new List(); + foreach (var plc in plcs) + { + for (int i = 0; i < Program.TaskNumberOfItems; i++) + { + tasks.Add(Task.Run(async () => + { + for (int i = 0; i < Program.NumberOfItems; i++) + { + var result = await plc.ReadAsync(DataType.Memory, 1, 0, VarType.Byte, 100); + } + })); + } + } + await Task.WhenAll(tasks); + } + + //并发失败 + //[Benchmark] + //public async Task HslCommunication() + //{ + // List tasks = new List(); + // foreach (var siemensS7Net in siemensS7Nets) + // { + // for (int i = 0; i < Program.TaskNumberOfItems; i++) + // { + // tasks.Add(Task.Run(async () => + // { + // for (int i = 0; i < Program.NumberOfItems; i++) + // { + // var result = await siemensS7Net.ReadAsync("M0", 100); + // if (!result.IsSuccess) + // { + // throw new Exception(result.Message); + // } + // } + // })); + // } + // } + // await Task.WhenAll(tasks); + //} + + + + + + + [Benchmark] + public async Task ThingsGateway() + { + SiemensS7Address[] siemensS7Address = [SiemensS7Address.ParseFrom("M1", 100)]; + List tasks = new List(); + foreach (var siemensS7 in siemensS7s) + { + for (int i = 0; i < Program.TaskNumberOfItems; i++) + { + tasks.Add(Task.Run(async () => + { + for (int i = 0; i < Program.NumberOfItems; i++) + { + var result = await siemensS7.S7ReadAsync(siemensS7Address); + if (!result.IsSuccess) + { + throw new Exception(result.ToString()); + } + } + })); + } + } + await Task.WhenAll(tasks); + } + + public void Dispose() + { + plcs.ForEach(a => a.SafeDispose()); + siemensS7Nets.ForEach(a => a.SafeDispose()); + siemensS7s.ForEach(a => a.Channel.SafeDispose()); + siemensS7s.ForEach(a => a.SafeDispose()); + } +} \ No newline at end of file diff --git a/src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/TimeoutBenchmark.cs b/src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/TimeoutBenchmark.cs new file mode 100644 index 000000000..811e9936e --- /dev/null +++ b/src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/TimeoutBenchmark.cs @@ -0,0 +1,62 @@ +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://kimdiego2098.github.io/ +// QQ群:605534569 +//------------------------------------------------------------------------------ + +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Diagnosers; + +using ThingsGateway.NewLife.Collections; + +namespace ThingsGateway.Foundation; + +[MemoryDiagnoser] +public class TimeoutBenchmark +{ + + + [Benchmark] + public async ValueTask CtsWaitAsync() + { + using var otherCts = new CancellationTokenSource(); + for (int i1 = 0; i1 < 10; i1++) + for (int i = 0; i < 10; i++) + { + using var ctsTime = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); + using var cts = CancellationTokenSource.CreateLinkedTokenSource(ctsTime.Token, otherCts.Token); + + await Task.Delay(5, cts.Token).ConfigureAwait(false); // 模拟工作 + + } + } + + private ObjectPool _reusableTimeouts; + [Benchmark] + public async ValueTask ReusableTimeoutWaitAsync() + { + _reusableTimeouts ??= new(); + using var otherCts = new CancellationTokenSource(); + for (int i1 = 0; i1 < 10; i1++) + for (int i = 0; i < 10; i++) + { + var _reusableTimeout = _reusableTimeouts.Get(); + try + { + await Task.Delay(5, _reusableTimeout.GetTokenSource(10, otherCts.Token).Token).ConfigureAwait(false); // 模拟工作 + } + finally + { + _reusableTimeouts.Return(_reusableTimeout); + } + } + + _reusableTimeouts.Dispose(); + } +} + + diff --git a/src/Plugin/ThingsGateway.Foundation.Benchmark/Program.cs b/src/Plugin/ThingsGateway.Foundation.Benchmark/Program.cs new file mode 100644 index 000000000..4027d0659 --- /dev/null +++ b/src/Plugin/ThingsGateway.Foundation.Benchmark/Program.cs @@ -0,0 +1,59 @@ +//------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// API首页:http://rrqm_home.gitee.io/touchsocket/ +// 交流QQ群:234762506 +// 感谢您的下载和使用 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Running; + +using ThingsGateway.Foundation; + +namespace BenchmarkConsoleApp +{ + internal class Program + { + public static int ClientCount = 30; + public static int TaskNumberOfItems = 30; + public static int NumberOfItems = 30; + + private static async Task Main(string[] args) + { + Console.WriteLine("开始测试前,请先启动ModbusSlave,建议使用本项目自带的ThingsGateway.Debug.Photino软件开启,S7可以用KEPSERVER的S7模拟服务"); + Console.WriteLine($"多客户端({ClientCount}),多线程({TaskNumberOfItems})并发读取({NumberOfItems})测试,共{ClientCount * TaskNumberOfItems * NumberOfItems}次"); + await Task.CompletedTask; + //ModbusBenchmark modbusBenchmark = new ModbusBenchmark(); + //System.Diagnostics.Stopwatch stopwatch = new(); + //stopwatch.Start(); + //await modbusBenchmark.ThingsGateway(); + //stopwatch.Stop(); + //Console.WriteLine($"ThingsGateway耗时:{stopwatch.ElapsedMilliseconds}ms"); + //stopwatch.Restart(); + //await modbusBenchmark.TouchSocket(); + //stopwatch.Stop(); + //Console.WriteLine($"TouchSocket耗时:{stopwatch.ElapsedMilliseconds}ms"); + //Console.ReadLine(); + + // BenchmarkRunner.Run( + //ManualConfig.Create(DefaultConfig.Instance) + //.WithOptions(ConfigOptions.DisableOptimizationsValidator) + //); + BenchmarkRunner.Run( + ManualConfig.Create(DefaultConfig.Instance) + .WithOptions(ConfigOptions.DisableOptimizationsValidator) + ); + BenchmarkRunner.Run( +ManualConfig.Create(DefaultConfig.Instance) +.WithOptions(ConfigOptions.DisableOptimizationsValidator) +); + + } + } +} \ No newline at end of file diff --git a/src/Plugin/ThingsGateway.Foundation.Benchmark/ThingsGateway.Foundation.Benchmark.csproj b/src/Plugin/ThingsGateway.Foundation.Benchmark/ThingsGateway.Foundation.Benchmark.csproj new file mode 100644 index 000000000..e255d138a --- /dev/null +++ b/src/Plugin/ThingsGateway.Foundation.Benchmark/ThingsGateway.Foundation.Benchmark.csproj @@ -0,0 +1,58 @@ + + + + Exe + net8.0 + + + + + + None + None + None + None + None + None + None + None + None + None + None + None + + + net8.0; + 13.0 + enable + enable + Diego + Diego + Diego + 版权所有 © 2023-present Diego + https://gitee.com/diego2098/ThingsGateway + Gitee + true + + + + + + + + + + + + + + + + + + + + + +