This commit is contained in:
2248356998 qq.com
2025-09-08 21:16:37 +08:00
parent aa247422d2
commit 0b829ac85c
16 changed files with 630 additions and 191 deletions

3
.gitignore vendored
View File

@@ -364,8 +364,5 @@ FodyWeavers.xsd
/src/*Pro*/ /src/*Pro*/
/src/*Pro* /src/*Pro*
/src/**/*Pro*
/src/*pro*
/src/*pro*/
/src/ThingsGateway.Server/Configuration/GiteeOAuthSettings.json /src/ThingsGateway.Server/Configuration/GiteeOAuthSettings.json
/src/.idea/ /src/.idea/

View File

@@ -1,5 +1,4 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Diagnostics;
using ThingsGateway.NewLife.Log; using ThingsGateway.NewLife.Log;
using ThingsGateway.NewLife.Reflection; using ThingsGateway.NewLife.Reflection;
@@ -76,7 +75,7 @@ public class ObjectPool<T> : DisposeBase, IPool<T> where T : notnull
_timer.TryDispose(); _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(); Clear();
} }
@@ -112,10 +111,6 @@ public class ObjectPool<T> : DisposeBase, IPool<T> where T : notnull
/// <returns></returns> /// <returns></returns>
public virtual T Get() public virtual T Get()
{ {
var sw = Log == null || Log == Logger.Null ? null : Stopwatch.StartNew();
Interlocked.Increment(ref _Total);
var success = false;
Item? pi = null; Item? pi = null;
do do
{ {
@@ -123,8 +118,6 @@ public class ObjectPool<T> : DisposeBase, IPool<T> where T : notnull
if (_free.TryPop(out pi) || _free2.TryDequeue(out pi)) if (_free.TryPop(out pi) || _free2.TryDequeue(out pi))
{ {
Interlocked.Decrement(ref _FreeCount); Interlocked.Decrement(ref _FreeCount);
success = true;
} }
else else
{ {
@@ -150,8 +143,6 @@ public class ObjectPool<T> : DisposeBase, IPool<T> where T : notnull
WriteLog("Acquire Create Free={0} Busy={1}", FreeCount, count + 1); WriteLog("Acquire Create Free={0} Busy={1}", FreeCount, count + 1);
#endif #endif
Interlocked.Increment(ref _NewCount);
success = false;
} }
// 借出时如果不可用,再次借取 // 借出时如果不可用,再次借取
@@ -164,17 +155,6 @@ public class ObjectPool<T> : DisposeBase, IPool<T> where T : notnull
_busy.TryAdd(pi.Value, pi); _busy.TryAdd(pi.Value, pi);
Interlocked.Increment(ref _BusyCount); 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; return pi.Value;
} }
@@ -200,7 +180,6 @@ public class ObjectPool<T> : DisposeBase, IPool<T> where T : notnull
#if DEBUG #if DEBUG
WriteLog("Return Error"); WriteLog("Return Error");
#endif #endif
Interlocked.Increment(ref _ReleaseCount);
return false; return false;
} }
@@ -210,13 +189,11 @@ public class ObjectPool<T> : DisposeBase, IPool<T> where T : notnull
// 是否可用 // 是否可用
if (!OnReturn(value)) if (!OnReturn(value))
{ {
Interlocked.Increment(ref _ReleaseCount);
return false; return false;
} }
if (value is DisposeBase db && db.Disposed) if (value is DisposeBase db && db.Disposed)
{ {
Interlocked.Increment(ref _ReleaseCount);
return false; return false;
} }
@@ -373,39 +350,14 @@ public class ObjectPool<T> : DisposeBase, IPool<T> where T : notnull
} }
} }
var ncount = _NewCount; if (count > 0)
var fcount = _ReleaseCount;
if (count > 0 || ncount > 0 || fcount > 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} 项。", FreeCount, BusyCount, count);
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);
} }
} }
#endregion #endregion
#region
private Int32 _Total;
/// <summary>总请求数</summary>
public Int32 Total => _Total;
private Int32 _Success;
/// <summary>成功数</summary>
public Int32 Success => _Success;
/// <summary>新创建数</summary>
private Int32 _NewCount;
/// <summary>释放数</summary>
private Int32 _ReleaseCount;
/// <summary>平均耗时。单位ms</summary>
private Double Cost;
#endregion
#region #region
/// <summary>日志</summary> /// <summary>日志</summary>
public ILog Log { get; set; } = Logger.Null; public ILog Log { get; set; } = Logger.Null;

View File

@@ -1,9 +1,9 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<PluginVersion>10.11.31</PluginVersion> <PluginVersion>10.11.33</PluginVersion>
<ProPluginVersion>10.11.31</ProPluginVersion> <ProPluginVersion>10.11.33</ProPluginVersion>
<DefaultVersion>10.11.31</DefaultVersion> <DefaultVersion>10.11.33</DefaultVersion>
<AuthenticationVersion>10.11.3</AuthenticationVersion> <AuthenticationVersion>10.11.3</AuthenticationVersion>
<SourceGeneratorVersion>10.11.3</SourceGeneratorVersion> <SourceGeneratorVersion>10.11.3</SourceGeneratorVersion>
<NET8Version>8.0.19</NET8Version> <NET8Version>8.0.19</NET8Version>

View File

@@ -29,17 +29,7 @@ public class OtherChannel : SetupConfigObject, IClientChannel
} }
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config; 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) 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 readonly WaitLock _connectLock = new WaitLock();
private IDeviceDataHandleAdapter _deviceDataHandleAdapter;
private bool logSet;
/// <inheritdoc/>
public void SetDataHandlingAdapterLogger(ILog log)
{
if (!logSet && ReadOnlyDataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
{
logSet = true;
handleAdapter.Logger = log;
}
}
/// <inheritdoc/> /// <inheritdoc/>
public void SetDataHandlingAdapter(DataHandlingAdapter adapter) public void SetDataHandlingAdapter(DataHandlingAdapter adapter)
{ {
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter) if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
SetAdapter(singleStreamDataHandlingAdapter); SetAdapter(singleStreamDataHandlingAdapter);
if (adapter is IDeviceDataHandleAdapter deviceDataHandleAdapter)
_deviceDataHandleAdapter = deviceDataHandleAdapter; logSet = false;
} }
/// <summary> /// <summary>
/// 设置数据处理适配器。 /// 设置数据处理适配器。
/// </summary> /// </summary>

View File

@@ -47,16 +47,15 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
/// <inheritdoc/> /// <inheritdoc/>
public DataHandlingAdapter ReadOnlyDataHandlingAdapter => ProtectedDataHandlingAdapter; public DataHandlingAdapter ReadOnlyDataHandlingAdapter => ProtectedDataHandlingAdapter;
private IDeviceDataHandleAdapter _deviceDataHandleAdapter;
private bool logSet;
/// <inheritdoc/>
public void SetDataHandlingAdapterLogger(ILog log) public void SetDataHandlingAdapterLogger(ILog log)
{ {
if (_deviceDataHandleAdapter != ProtectedDataHandlingAdapter && ProtectedDataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter) if (!logSet && ProtectedDataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
{ {
_deviceDataHandleAdapter = handleAdapter; logSet = true;
} handleAdapter.Logger = log;
if (_deviceDataHandleAdapter != null)
{
_deviceDataHandleAdapter.Logger = log;
} }
} }
/// <inheritdoc/> /// <inheritdoc/>
@@ -64,10 +63,11 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
{ {
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter) if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
SetAdapter(singleStreamDataHandlingAdapter); SetAdapter(singleStreamDataHandlingAdapter);
if (adapter is IDeviceDataHandleAdapter deviceDataHandleAdapter)
_deviceDataHandleAdapter = deviceDataHandleAdapter; logSet = false;
} }
/// <inheritdoc/> /// <inheritdoc/>
public ChannelEventHandler Started { get; } = new(); public ChannelEventHandler Started { get; } = new();

View File

@@ -30,16 +30,13 @@ public class TcpClientChannel : TcpClient, IClientChannel
WaitHandlePool = new WaitHandlePool<MessageBase>(minSign, maxSign); WaitHandlePool = new WaitHandlePool<MessageBase>(minSign, maxSign);
pool?.CancelAll(); pool?.CancelAll();
} }
private IDeviceDataHandleAdapter _deviceDataHandleAdapter; private bool logSet;
public void SetDataHandlingAdapterLogger(ILog log) public void SetDataHandlingAdapterLogger(ILog log)
{ {
if (_deviceDataHandleAdapter != DataHandlingAdapter && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter) if (!logSet && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
{ {
_deviceDataHandleAdapter = handleAdapter; logSet = true;
} handleAdapter.Logger = log;
if (_deviceDataHandleAdapter != null)
{
_deviceDataHandleAdapter.Logger = log;
} }
} }
/// <inheritdoc/> /// <inheritdoc/>
@@ -47,8 +44,8 @@ public class TcpClientChannel : TcpClient, IClientChannel
{ {
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter) if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
SetAdapter(singleStreamDataHandlingAdapter); SetAdapter(singleStreamDataHandlingAdapter);
if (adapter is IDeviceDataHandleAdapter deviceDataHandleAdapter)
_deviceDataHandleAdapter = deviceDataHandleAdapter; logSet = false;
} }
/// <inheritdoc/> /// <inheritdoc/>
public ChannelReceivedEventHandler ChannelReceived { get; } = new(); public ChannelReceivedEventHandler ChannelReceived { get; } = new();

View File

@@ -21,16 +21,14 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel
public TcpSessionClientChannel() public TcpSessionClientChannel()
{ {
} }
private IDeviceDataHandleAdapter _deviceDataHandleAdapter; private bool logSet;
/// <inheritdoc/>
public void SetDataHandlingAdapterLogger(ILog log) public void SetDataHandlingAdapterLogger(ILog log)
{ {
if (_deviceDataHandleAdapter != DataHandlingAdapter && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter) if (!logSet && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
{ {
_deviceDataHandleAdapter = handleAdapter; logSet = true;
} handleAdapter.Logger = log;
if (_deviceDataHandleAdapter != null)
{
_deviceDataHandleAdapter.Logger = log;
} }
} }
/// <inheritdoc/> /// <inheritdoc/>
@@ -38,9 +36,10 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel
{ {
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter) if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
SetAdapter(singleStreamDataHandlingAdapter); SetAdapter(singleStreamDataHandlingAdapter);
if (adapter is IDeviceDataHandleAdapter deviceDataHandleAdapter)
_deviceDataHandleAdapter = deviceDataHandleAdapter; logSet = false;
} }
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue) public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
{ {
var pool = WaitHandlePool; var pool = WaitHandlePool;

View File

@@ -26,16 +26,14 @@ public class UdpSessionChannel : UdpSession, IClientChannel
ResetSign(); ResetSign();
} }
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config; public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
private IDeviceDataHandleAdapter _deviceDataHandleAdapter; private bool logSet;
/// <inheritdoc/>
public void SetDataHandlingAdapterLogger(ILog log) public void SetDataHandlingAdapterLogger(ILog log)
{ {
if (_deviceDataHandleAdapter != DataHandlingAdapter && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter) if (!logSet && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
{ {
_deviceDataHandleAdapter = handleAdapter; logSet = true;
} handleAdapter.Logger = log;
if (_deviceDataHandleAdapter != null)
{
_deviceDataHandleAdapter.Logger = log;
} }
} }
/// <inheritdoc/> /// <inheritdoc/>
@@ -43,9 +41,11 @@ public class UdpSessionChannel : UdpSession, IClientChannel
{ {
if (adapter is UdpDataHandlingAdapter udpDataHandlingAdapter) if (adapter is UdpDataHandlingAdapter udpDataHandlingAdapter)
SetAdapter(udpDataHandlingAdapter); SetAdapter(udpDataHandlingAdapter);
if (adapter is IDeviceDataHandleAdapter deviceDataHandleAdapter)
_deviceDataHandleAdapter = deviceDataHandleAdapter; logSet = false;
} }
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue) public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
{ {
var pool = WaitHandlePool; var pool = WaitHandlePool;

View File

@@ -331,17 +331,12 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
} }
public bool AutoConnect { get; protected set; } = true; public bool AutoConnect { get; protected set; } = true;
/// <inheritdoc/> /// <inheritdoc/>
private async ValueTask<OperResult> SendAsync(ISendMessage sendMessage, IClientChannel channel, CancellationToken token = default) private async Task SendAsync(ISendMessage sendMessage, IClientChannel channel, CancellationToken token = default)
{
try
{ {
if (SendDelayTime != 0) if (SendDelayTime != 0)
await Task.Delay(SendDelayTime, token).ConfigureAwait(false); await Task.Delay(SendDelayTime, token).ConfigureAwait(false);
if (token.IsCancellationRequested)
return new OperResult(new OperationCanceledException());
if (channel is IDtuUdpSessionChannel udpSession) if (channel is IDtuUdpSessionChannel udpSession)
{ {
EndPoint? endPoint = GetUdpEndpoint(); EndPoint? endPoint = GetUdpEndpoint();
@@ -353,12 +348,6 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
await channel.SendAsync(sendMessage, token).ConfigureAwait(false); await channel.SendAsync(sendMessage, token).ConfigureAwait(false);
} }
return OperResult.Success;
}
catch (Exception ex)
{
return new(ex);
}
} }
private Task BeforeSendAsync(IClientChannel channel, CancellationToken token) private Task BeforeSendAsync(IClientChannel channel, CancellationToken token)
@@ -417,7 +406,8 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
channelResult.Content.SetDataHandlingAdapterLogger(Logger); 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 finally
{ {
@@ -426,8 +416,6 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
} }
catch (Exception ex) catch (Exception ex)
{ {
if (!cancellationToken.IsCancellationRequested)
await Task.Delay(1000, cancellationToken).ConfigureAwait(false);
return new(ex); return new(ex);
} }
} }
@@ -538,7 +526,6 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
} }
private ObjectPool<ReusableCancellationTokenSource> _reusableTimeouts = new(); private ObjectPool<ReusableCancellationTokenSource> _reusableTimeouts = new();
/// <summary> /// <summary>
/// 发送并等待数据 /// 发送并等待数据
/// </summary> /// </summary>
@@ -548,6 +535,8 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
int timeout = 3000, int timeout = 3000,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
var waitData = clientChannel.WaitHandlePool.GetWaitDataAsync(out var sign); var waitData = clientChannel.WaitHandlePool.GetWaitDataAsync(out var sign);
command.Sign = sign; command.Sign = sign;
WaitLock? waitLock = null; WaitLock? waitLock = null;
@@ -557,13 +546,12 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
await BeforeSendAsync(clientChannel, cancellationToken).ConfigureAwait(false); await BeforeSendAsync(clientChannel, cancellationToken).ConfigureAwait(false);
waitLock = GetWaitLock(clientChannel); waitLock = GetWaitLock(clientChannel);
await waitLock.WaitAsync(cancellationToken).ConfigureAwait(false); await waitLock.WaitAsync(cancellationToken).ConfigureAwait(false);
clientChannel.SetDataHandlingAdapterLogger(Logger); clientChannel.SetDataHandlingAdapterLogger(Logger);
var sendResult = await SendAsync(command, clientChannel, cancellationToken).ConfigureAwait(false); await SendAsync(command, clientChannel, cancellationToken).ConfigureAwait(false);
if (!sendResult.IsSuccess)
return new MessageBase(sendResult);
if (waitData.Status == WaitDataStatus.Success) if (waitData.Status == WaitDataStatus.Success)
return waitData.CompletedData; return waitData.CompletedData;
@@ -573,6 +561,7 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
var reusableTimeout = _reusableTimeouts.Get(); var reusableTimeout = _reusableTimeouts.Get();
try try
{ {
var cts = reusableTimeout.GetTokenSource(timeout, cancellationToken, Channel.ClosedToken); var cts = reusableTimeout.GetTokenSource(timeout, cancellationToken, Channel.ClosedToken);
await waitData.WaitAsync(cts.Token).ConfigureAwait(false); await waitData.WaitAsync(cts.Token).ConfigureAwait(false);
} }
@@ -606,6 +595,7 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
{ {
waitLock?.Release(); waitLock?.Release();
waitData?.SafeDispose(); waitData?.SafeDispose();
} }
} }

View File

@@ -8,35 +8,48 @@
// QQ群605534569 // QQ群605534569
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
namespace ThingsGateway.Gateway.Application; namespace ThingsGateway.Foundation;
public class LinkedCancellationTokenSourceCache : IDisposable public class LinkedCancellationTokenSourceCache : IDisposable
{ {
private CancellationTokenSource? _cachedCts; private CancellationTokenSource? _cachedCts;
private CancellationToken _token1; private CancellationToken _token1;
private CancellationToken _token2; private CancellationToken _token2;
private CancellationToken _token3;
private readonly object _lock = new(); private readonly object _lock = new();
~LinkedCancellationTokenSourceCache() ~LinkedCancellationTokenSourceCache()
{ {
Dispose(); Dispose();
} }
/// <summary> /// <summary>
/// 获取一个 CancellationTokenSource它是由两个 token 链接而成的。 /// 获取一个 CancellationTokenSource它是由两个 token 链接而成的。
/// 会尝试复用之前缓存的 CTS前提是两个 token 仍然相同且未取消。 /// 会尝试复用之前缓存的 CTS前提是两个 token 仍然相同且未取消。
/// </summary> /// </summary>
public CancellationTokenSource GetLinkedTokenSource(CancellationToken token1, CancellationToken token2) public CancellationTokenSource GetLinkedTokenSource(CancellationToken token1, CancellationToken token2, CancellationToken token3 = default)
{ {
lock (_lock) lock (_lock)
{ {
// 如果缓存的 CTS 已经取消或 Dispose或者 token 不同,重新创建 // 如果缓存的 CTS 已经取消或 Dispose或者 token 不同,重新创建
if (_cachedCts?.IsCancellationRequested != false || 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?.Dispose();
_cachedCts = CancellationTokenSource.CreateLinkedTokenSource(token1, token2, token3);
}
#else
_cachedCts?.Dispose();
_cachedCts = CancellationTokenSource.CreateLinkedTokenSource(token1, token2, token3);
#endif
_cachedCts = CancellationTokenSource.CreateLinkedTokenSource(token1, token2);
_token1 = token1; _token1 = token1;
_token2 = token2; _token2 = token2;
_token3 = token3;
} }
return _cachedCts; return _cachedCts;

View File

@@ -18,7 +18,6 @@ using System.Threading;
public sealed class ReusableCancellationTokenSource : IDisposable public sealed class ReusableCancellationTokenSource : IDisposable
{ {
private readonly Timer _timer; private readonly Timer _timer;
private readonly object _lock = new();
private CancellationTokenSource? _cts; private CancellationTokenSource? _cts;
public ReusableCancellationTokenSource() public ReusableCancellationTokenSource()
@@ -29,8 +28,6 @@ public sealed class ReusableCancellationTokenSource : IDisposable
public bool TimeoutStatus = false; public bool TimeoutStatus = false;
private void OnTimeout(object? state) private void OnTimeout(object? state)
{
lock (_lock)
{ {
TimeoutStatus = true; TimeoutStatus = true;
@@ -38,52 +35,24 @@ public sealed class ReusableCancellationTokenSource : IDisposable
_cts?.Cancel(); _cts?.Cancel();
} }
}
/// <summary>
/// 获取一个 CTS并启动超时
/// </summary>
public CancellationTokenSource GetTokenSource(long timeout, CancellationToken external1 = default)
{
lock (_lock)
{
TimeoutStatus = false;
// 如果已有 CTS先 Dispose private readonly LinkedCancellationTokenSourceCache _linkedCtsCache = new();
_cts?.SafeCancel();
_cts?.SafeDispose();
// 创建新的 CTS
_cts = CancellationTokenSource.CreateLinkedTokenSource(external1);
// 启动 Timer
_timer.Change(timeout, Timeout.Infinite);
return _cts;
}
}
/// <summary> /// <summary>
/// 获取一个 CTS并启动超时 /// 获取一个 CTS并启动超时
/// </summary> /// </summary>
public CancellationTokenSource GetTokenSource(long timeout, CancellationToken external1 = default, CancellationToken external2 = default, CancellationToken external3 = default) 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
_cts = CancellationTokenSource.CreateLinkedTokenSource(external1, external2, external3); _cts = _linkedCtsCache.GetLinkedTokenSource(external1, external2, external3);
// 启动 Timer // 启动 Timer
_timer.Change(timeout, Timeout.Infinite); _timer.Change(timeout, Timeout.Infinite);
return _cts; return _cts;
} }
}
public void Set() public void Set()
@@ -95,23 +64,18 @@ public sealed class ReusableCancellationTokenSource : IDisposable
/// 手动取消 /// 手动取消
/// </summary> /// </summary>
public void Cancel() public void Cancel()
{
lock (_lock)
{ {
_cts?.SafeCancel(); _cts?.SafeCancel();
} }
}
public void Dispose() public void Dispose()
{
lock (_lock)
{ {
_cts?.SafeCancel(); _cts?.SafeCancel();
_cts?.SafeDispose(); _cts?.SafeDispose();
_linkedCtsCache.SafeDispose();
_timer.SafeDispose(); _timer.SafeDispose();
} }
} }
}

View File

@@ -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<ModbusMaster> thingsgatewaymodbuss = new();
private List<IModbusMaster> nmodbuss = new();
private List<ModbusTcpNet> modbusTcpNets = new();
private List<ModbusTcpMaster> 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<Task> tasks = new List<Task>();
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<Task> tasks = new List<Task>();
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<Task> tasks = new List<Task>();
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<Task> tasks = new List<Task>();
// 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());
}
}

View File

@@ -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<SiemensS7Master> siemensS7s = new();
private List<Plc> plcs = new();
private List<SiemensS7Net> 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<Task> tasks = new List<Task>();
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<Task> tasks = new List<Task>();
// 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<Task> tasks = new List<Task>();
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());
}
}

View File

@@ -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<ReusableCancellationTokenSource> _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();
}
}

View File

@@ -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<TimeoutBenchmark>(
//ManualConfig.Create(DefaultConfig.Instance)
//.WithOptions(ConfigOptions.DisableOptimizationsValidator)
//);
BenchmarkRunner.Run<ModbusBenchmark>(
ManualConfig.Create(DefaultConfig.Instance)
.WithOptions(ConfigOptions.DisableOptimizationsValidator)
);
BenchmarkRunner.Run<S7Benchmark>(
ManualConfig.Create(DefaultConfig.Instance)
.WithOptions(ConfigOptions.DisableOptimizationsValidator)
);
}
}
}

View File

@@ -0,0 +1,58 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<AnalysisModeDesign>None</AnalysisModeDesign>
<AnalysisModeDocumentation>None</AnalysisModeDocumentation>
<AnalysisModeGlobalization>None</AnalysisModeGlobalization>
<AnalysisModeInteroperability>None</AnalysisModeInteroperability>
<AnalysisModeMaintainability>None</AnalysisModeMaintainability>
<AnalysisModeNaming>None</AnalysisModeNaming>
<AnalysisModePerformance>None</AnalysisModePerformance>
<AnalysisModeSingleFile>None</AnalysisModeSingleFile>
<AnalysisModeReliability>None</AnalysisModeReliability>
<AnalysisModeSecurity>None</AnalysisModeSecurity>
<AnalysisModeUsage>None</AnalysisModeUsage>
<AnalysisModeStyle>None</AnalysisModeStyle>
<TargetFrameworks>net8.0;</TargetFrameworks>
<LangVersion>13.0</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Authors>Diego</Authors>
<Company>Diego</Company>
<Product>Diego</Product>
<Copyright>版权所有 © 2023-present Diego</Copyright>
<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl>
<RepositoryType>Gitee</RepositoryType>
<GenerateResxSourceIncludeDefaultValues>true</GenerateResxSourceIncludeDefaultValues>
</PropertyGroup>
<ItemGroup>
<PackageReference Remove="Roslynator.Analyzers">
</PackageReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.15.2" />
<PackageReference Include="HslCommunication" Version="12.3.3" />
<PackageReference Include="NModbus" Version="3.0.81" />
<PackageReference Include="NModbus.Serial" Version="3.0.81" />
<PackageReference Include="S7netplus" Version="0.20.0" />
<PackageReference Include="ThingsGateway.Foundation.Modbus" Version="10.11.32" />
<PackageReference Include="ThingsGateway.Foundation.SiemensS7" Version="10.11.32" />
<PackageReference Include="TouchSocket.Modbus" Version="4.0.0-beta.25" />
</ItemGroup>
<ItemGroup>
<!--<ProjectReference Include="..\..\Plugin\ThingsGateway.Foundation.Modbus\ThingsGateway.Foundation.Modbus.csproj" />
<ProjectReference Include="..\..\Plugin\ThingsGateway.Foundation.SiemensS7\ThingsGateway.Foundation.SiemensS7.csproj" />-->
</ItemGroup>
</Project>