diff --git a/src/Admin/ThingsGateway.NewLife.X/Reflection/Reflect.cs b/src/Admin/ThingsGateway.NewLife.X/Reflection/Reflect.cs
index f7b290317..f919cdcfb 100644
--- a/src/Admin/ThingsGateway.NewLife.X/Reflection/Reflect.cs
+++ b/src/Admin/ThingsGateway.NewLife.X/Reflection/Reflect.cs
@@ -580,7 +580,19 @@ public static class Reflect
return func;
}
+ /// 把一个方法转为泛型委托,便于快速反射调用
+ ///
+ ///
+ ///
+ ///
+ public static void RemoveCache(this MethodInfo method, object? target = null) where TFunc : class
+ {
+ if (method == null) return;
+ var key = new DelegateCacheKey(method, typeof(TFunc), target);
+
+ DelegateCache.Cache.TryRemove(key);
+ }
#endregion
}
public static class DelegateCache
diff --git a/src/Admin/ThingsGateway.NewLife.X/Threading/TimerScheduler.cs b/src/Admin/ThingsGateway.NewLife.X/Threading/TimerScheduler.cs
index 65778c688..35c5b043f 100644
--- a/src/Admin/ThingsGateway.NewLife.X/Threading/TimerScheduler.cs
+++ b/src/Admin/ThingsGateway.NewLife.X/Threading/TimerScheduler.cs
@@ -191,6 +191,13 @@ public class TimerScheduler : IDisposable, ILogFeature
Count--;
}
}
+
+ timer.Method.RemoveCache(timer.Target.Target);
+#if NET6_0_OR_GREATER
+ timer.Method.RemoveCache>(timer.Target.Target);
+#endif
+ timer.Method.RemoveCache>(timer.Target.Target);
+
}
private AutoResetEvent? _waitForTimer;
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 4dd9fd735..e0f66db04 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -1,9 +1,9 @@
- 10.11.114
- 10.11.114
- 10.11.114
+ 10.11.115
+ 10.11.115
+ 10.11.115
10.11.6
10.11.6
8.0.21
diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Controller/ControlController.cs b/src/Gateway/ThingsGateway.Gateway.Application/Controller/ControlController.cs
index ebd710151..b926fc513 100644
--- a/src/Gateway/ThingsGateway.Gateway.Application/Controller/ControlController.cs
+++ b/src/Gateway/ThingsGateway.Gateway.Application/Controller/ControlController.cs
@@ -15,6 +15,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel;
+using ThingsGateway.NewLife.DictionaryExtensions;
using ThingsGateway.FriendlyException;
@@ -122,16 +123,11 @@ public class ControlController : ControllerBase, IRpcServer
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public async Task>> WriteVariablesAsync([FromBody][TouchSocket.WebApi.FromBody] Dictionary> deviceDatas)
{
- foreach (var deviceData in deviceDatas)
- {
- if (GlobalData.Devices.TryGetValue(deviceData.Key, out var device))
- {
- var data = device.VariableRuntimes.Where(a => deviceData.Value.ContainsKey(a.Key)).ToList();
- await GlobalData.SysUserService.CheckApiDataScopeAsync(data.Select(a => a.Value.CreateOrgId), data.Select(a => a.Value.CreateUserId)).ConfigureAwait(false);
- }
- }
+ await GlobalData.CheckByDeviceNames(deviceDatas.Select(a => a.Key)).ConfigureAwait(false);
return (await GlobalData.RpcService.InvokeDeviceMethodAsync($"WebApi-{UserManager.UserAccount}-{App.HttpContext?.GetRemoteIpAddressToIPv4()}", deviceDatas).ConfigureAwait(false)).ToDictionary(a => a.Key, a => a.Value.ToDictionary(b => b.Key, b => (OperResult)b.Value));
+
+
}
///
diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Entity/Variable.cs b/src/Gateway/ThingsGateway.Gateway.Application/Entity/Variable.cs
index a5bf7fd1a..661dc68e2 100644
--- a/src/Gateway/ThingsGateway.Gateway.Application/Entity/Variable.cs
+++ b/src/Gateway/ThingsGateway.Gateway.Application/Entity/Variable.cs
@@ -28,7 +28,7 @@ namespace ThingsGateway.Gateway.Application;
[SugarIndex("index_device", nameof(Variable.DeviceId), OrderByType.Asc)]
[SugarIndex("unique_deviceid_variable_name", nameof(Variable.Name), OrderByType.Asc, nameof(Variable.DeviceId), OrderByType.Asc, true)]
#endif
-public class Variable : BaseDataEntity, IValidatableObject
+public class Variable : PrimaryKeyEntity, IValidatableObject
{
///
/// 主键Id
diff --git a/src/Gateway/ThingsGateway.Gateway.Application/GlobalData/GlobalData.cs b/src/Gateway/ThingsGateway.Gateway.Application/GlobalData/GlobalData.cs
index f504af2b8..65e02f7a5 100644
--- a/src/Gateway/ThingsGateway.Gateway.Application/GlobalData/GlobalData.cs
+++ b/src/Gateway/ThingsGateway.Gateway.Application/GlobalData/GlobalData.cs
@@ -15,6 +15,7 @@ using PooledAwait;
using System.Collections.Concurrent;
using ThingsGateway.Extension.Generic;
+using ThingsGateway.NewLife.DictionaryExtensions;
namespace ThingsGateway.Gateway.Application;
@@ -109,11 +110,15 @@ public static class GlobalData
static async PooledTask> GetCurrentUserDevices()
{
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
- return ReadOnlyIdDevices.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询
+ return IdDevices.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询
.WhereIf(dataScope?.Count == 0, u => u.Value.CreateUserId == UserManager.UserId).Select(a => a.Value);
}
}
-
+ public static IEnumerable GetCurrentUserDeviceIds(HashSet dataScope)
+ {
+ return IdDevices.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询
+ .WhereIf(dataScope?.Count == 0, u => u.Value.CreateUserId == UserManager.UserId).Select(a => a.Key);
+ }
public static Task> GetCurrentUserIdVariables()
{
return GetCurrentUserIdVariables();
@@ -121,11 +126,53 @@ public static class GlobalData
static async PooledTask> GetCurrentUserIdVariables()
{
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
- return IdVariables.Where(a => a.Value.IsInternalMemoryVariable == false).WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询
- .WhereIf(dataScope?.Count == 0, u => u.Value.CreateUserId == UserManager.UserId).Select(a => a.Value);
+
+ return IdVariables.Where(a => a.Value.IsInternalMemoryVariable == false)
+ .WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.DeviceRuntime.CreateOrgId))//在指定机构列表查询
+ .WhereIf(dataScope?.Count == 0, u => u.Value.DeviceRuntime.CreateUserId == UserManager.UserId).Select(a => a.Value);
}
}
+ public static async Task CheckByDeviceNames(IEnumerable deviceNames)
+ {
+ List orgids = new();
+ List userIds = new();
+ foreach (var deviceData in GlobalData.Devices.FilterByKeys(deviceNames))
+ {
+ orgids.Add(deviceData.Value.CreateOrgId);
+ userIds.Add(deviceData.Value.CreateUserId);
+ }
+ await GlobalData.SysUserService.CheckApiDataScopeAsync(orgids, userIds).ConfigureAwait(false);
+ }
+ public static async Task CheckByDeviceIds(IEnumerable deviceIds)
+ {
+ List orgids = new();
+ List userIds = new();
+ foreach (var deviceData in GlobalData.IdDevices.FilterByKeys(deviceIds))
+ {
+ orgids.Add(deviceData.Value.CreateOrgId);
+ userIds.Add(deviceData.Value.CreateUserId);
+ }
+ await GlobalData.SysUserService.CheckApiDataScopeAsync(orgids, userIds).ConfigureAwait(false);
+ }
+ public static async Task CheckByVariableIds(IEnumerable variableIds)
+ {
+ List orgids = new();
+ List userIds = new();
+ foreach (var deviceData in GlobalData.IdVariables.FilterByKeys(variableIds))
+ {
+ orgids.Add(deviceData.Value.DeviceRuntime.CreateOrgId);
+ userIds.Add(deviceData.Value.DeviceRuntime.CreateUserId);
+ }
+ await GlobalData.SysUserService.CheckApiDataScopeAsync(orgids, userIds).ConfigureAwait(false);
+ }
+ public static async Task CheckByVariableId(long variableId)
+ {
+ if (GlobalData.IdVariables.TryGetValue(variableId, out var variable))
+ {
+ await GlobalData.SysUserService.CheckApiDataScopeAsync(variable.DeviceRuntime.CreateOrgId, variable.DeviceRuntime.CreateUserId).ConfigureAwait(false);
+ }
+ }
public static Task> GetCurrentUserRealAlarmVariablesAsync()
{
return GetCurrentUserRealAlarmVariablesAsync();
@@ -133,7 +180,8 @@ public static class GlobalData
static async PooledTask> GetCurrentUserRealAlarmVariablesAsync()
{
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
- return RealAlarmIdVariables.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询
+ return RealAlarmIdVariables
+ .WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询
.WhereIf(dataScope?.Count == 0, u => u.Value.CreateUserId == UserManager.UserId).Select(a => a.Value);
}
}
@@ -145,8 +193,8 @@ public static class GlobalData
static async PooledTask> GetCurrentUserAlarmEnableVariables()
{
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
- return AlarmEnableIdVariables.Where(a => a.Value.IsInternalMemoryVariable == false).WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询
- .WhereIf(dataScope?.Count == 0, u => u.Value.CreateUserId == UserManager.UserId).Select(a => a.Value);
+ return AlarmEnableIdVariables.Where(a => a.Value.IsInternalMemoryVariable == false).WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.DeviceRuntime.CreateOrgId))//在指定机构列表查询
+ .WhereIf(dataScope?.Count == 0, u => u.Value.DeviceRuntime.CreateUserId == UserManager.UserId).Select(a => a.Value);
}
}
diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Mapper/GatewayMapper.cs b/src/Gateway/ThingsGateway.Gateway.Application/Mapper/GatewayMapper.cs
index d952a42ce..7475a4af9 100644
--- a/src/Gateway/ThingsGateway.Gateway.Application/Mapper/GatewayMapper.cs
+++ b/src/Gateway/ThingsGateway.Gateway.Application/Mapper/GatewayMapper.cs
@@ -26,6 +26,8 @@ public static partial class GatewayMapper
[MapProperty($"{nameof(VariableRuntime.AlarmRuntimePropertys)}.{nameof(AlarmRuntimePropertys.EventTime)}", nameof(AlarmVariable.EventTime))]
[MapProperty($"{nameof(VariableRuntime.AlarmRuntimePropertys)}.{nameof(AlarmRuntimePropertys.AlarmType)}", nameof(AlarmVariable.AlarmType))]
[MapProperty($"{nameof(VariableRuntime.AlarmRuntimePropertys)}.{nameof(AlarmRuntimePropertys.EventType)}", nameof(AlarmVariable.EventType))]
+ [MapProperty($"{nameof(VariableRuntime.DeviceRuntime)}.{nameof(DeviceRuntime.CreateOrgId)}", nameof(AlarmVariable.CreateOrgId))]
+ [MapProperty($"{nameof(VariableRuntime.DeviceRuntime)}.{nameof(DeviceRuntime.CreateUserId)}", nameof(AlarmVariable.CreateUserId))]
public static partial AlarmVariable AdaptAlarmVariable(this VariableRuntime src);
public static partial VariableDataWithValue AdaptVariableDataWithValue(this VariableBasicData src);
diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Model/VariableRunTime.cs b/src/Gateway/ThingsGateway.Gateway.Application/Model/VariableRunTime.cs
index a23635f10..5e536143c 100644
--- a/src/Gateway/ThingsGateway.Gateway.Application/Model/VariableRunTime.cs
+++ b/src/Gateway/ThingsGateway.Gateway.Application/Model/VariableRunTime.cs
@@ -66,11 +66,6 @@ public partial class VariableRuntime : Variable
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 5)]
public DateTime CollectTime { get => collectTime; set => collectTime = value; }
- [SugarColumn(ColumnDescription = "排序码", IsNullable = true)]
- [AutoGenerateColumn(Visible = false, DefaultSort = false, Sortable = true)]
- [IgnoreExcel]
- public override int SortCode { get => sortCode; set => sortCode = value; }
-
///
/// 上次值
///
@@ -245,7 +240,6 @@ public partial class VariableRuntime : Variable
private int index;
- private int sortCode;
private DateTime changeTime = DateTime.UnixEpoch.ToLocalTime();
private DateTime collectTime = DateTime.UnixEpoch.ToLocalTime();
diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Services/Variable/VariableService.cs b/src/Gateway/ThingsGateway.Gateway.Application/Services/Variable/VariableService.cs
index 0123757c6..a7baef68b 100644
--- a/src/Gateway/ThingsGateway.Gateway.Application/Services/Variable/VariableService.cs
+++ b/src/Gateway/ThingsGateway.Gateway.Application/Services/Variable/VariableService.cs
@@ -22,7 +22,6 @@ using System.Text;
using ThingsGateway.Common.Extension;
using ThingsGateway.Common.Extension.Generic;
-using ThingsGateway.Extension.Generic;
using ThingsGateway.Foundation.Extension.Dynamic;
using TouchSocket.Core;
@@ -107,8 +106,6 @@ internal sealed class VariableService : BaseService, IVariableService
variable.DataType = DataTypeEnum.Int16;
variable.Name = name;
variable.Id = id;
- variable.CreateOrgId = UserManager.OrgId;
- variable.CreateUserId = UserManager.UserId;
variable.DeviceId = device.Id;
variable.RegisterAddress = address;
newVariables.Add(variable);
@@ -334,8 +331,6 @@ internal sealed class VariableService : BaseService, IVariableService
variable.DataType = DataTypeEnum.Int16;
variable.Name = name;
variable.Id = id;
- variable.CreateOrgId = UserManager.OrgId;
- variable.CreateUserId = UserManager.UserId;
variable.DeviceId = device.Id;
variable.RegisterAddress = address;
newVariables.Add(variable);
@@ -428,12 +423,9 @@ internal sealed class VariableService : BaseService, IVariableService
differences.Remove(nameof(Variable.VariablePropertys));
if (differences?.Count > 0)
{
+ var data = models.ToList();
+ await GlobalData.CheckByDeviceIds(data.Select(a => a.DeviceId)).ConfigureAwait(false);
using var db = GetDB();
- var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
- var data = models
- .WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.CreateOrgId))//在指定机构列表查询
- .WhereIf(dataScope?.Count == 0, u => u.CreateUserId == UserManager.UserId)
- .ToList();
var result = (await db.Updateable(data).UpdateColumns(differences.Select(a => a.Key).ToArray()).ExecuteCommandAsync().ConfigureAwait(false)) > 0;
@@ -448,24 +440,20 @@ internal sealed class VariableService : BaseService, IVariableService
[OperDesc("DeleteVariable", isRecordPar: false, localizerType: typeof(Variable))]
public async Task DeleteByDeviceIdAsync(IEnumerable input, SqlSugarClient db)
{
- var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
var ids = input.ToList();
- var result = await db.Deleteable().Where(a => ids.Contains(a.DeviceId))
- .WhereIF(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.CreateOrgId))//在指定机构列表查询
- .WhereIF(dataScope?.Count == 0, u => u.CreateUserId == UserManager.UserId)
- .ExecuteCommandAsync().ConfigureAwait(false);
+ await GlobalData.CheckByDeviceIds(ids).ConfigureAwait(false);
+ var result = await db.Deleteable().Where(a => ids.Contains(a.DeviceId))
+ .ExecuteCommandAsync().ConfigureAwait(false);
}
[OperDesc("DeleteVariable", isRecordPar: false, localizerType: typeof(Variable))]
public async Task DeleteVariableAsync(IEnumerable input)
{
using var db = GetDB();
- var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
var ids = input?.ToList();
+ await GlobalData.CheckByVariableIds(ids).ConfigureAwait(false);
var result = (await db.Deleteable().WhereIF(input != null, a => ids.Contains(a.Id))
- .WhereIF(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.CreateOrgId))//在指定机构列表查询
- .WhereIF(dataScope?.Count == 0, u => u.CreateUserId == UserManager.UserId)
.ExecuteCommandAsync().ConfigureAwait(false)) > 0;
return result;
@@ -505,6 +493,11 @@ internal sealed class VariableService : BaseService, IVariableService
private async Task, ISugarQueryable>> GetWhereQueryFunc(GatewayExportFilter exportFilter)
{
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
+ List? filterDeviceIds= null;
+ if(dataScope!=null)
+ {
+ filterDeviceIds= GlobalData.GetCurrentUserDeviceIds(dataScope).ToList();
+ }
HashSet? deviceId = null;
if (!exportFilter.PluginName.IsNullOrWhiteSpace())
{
@@ -520,8 +513,7 @@ internal sealed class VariableService : BaseService, IVariableService
.WhereIF(exportFilter.PluginType == PluginTypeEnum.Collect, a => a.DeviceId == exportFilter.DeviceId)
.WhereIF(deviceId != null, a => deviceId.Contains(a.DeviceId))
- .WhereIF(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.CreateOrgId))//在指定机构列表查询
- .WhereIF(dataScope?.Count == 0, u => u.CreateUserId == UserManager.UserId)
+ .WhereIF(filterDeviceIds != null , u => filterDeviceIds.Contains(u.DeviceId))//在指定机构列表查询
.WhereIF(exportFilter.PluginType == PluginTypeEnum.Business, u => SqlFunc.JsonLike(u.VariablePropertys, exportFilter.DeviceId.ToString()));
return whereQuery;
@@ -530,6 +522,13 @@ internal sealed class VariableService : BaseService, IVariableService
private async Task, IEnumerable>> GetWhereEnumerableFunc(GatewayExportFilter exportFilter, bool sql = false)
{
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
+ List? filterDeviceIds = null;
+ if (dataScope != null)
+ {
+ filterDeviceIds = GlobalData.GetCurrentUserDeviceIds(dataScope).ToList();
+ }
+
+
HashSet? deviceId = null;
if (!exportFilter.PluginName.IsNullOrWhiteSpace())
{
@@ -545,8 +544,7 @@ internal sealed class VariableService : BaseService, IVariableService
.WhereIF(exportFilter.PluginType == PluginTypeEnum.Collect, a => a.DeviceId == exportFilter.DeviceId)
.WhereIF(deviceId != null, a => deviceId.Contains(a.DeviceId))
- .WhereIF(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.CreateOrgId))//在指定机构列表查询
- .WhereIF(dataScope?.Count == 0, u => u.CreateUserId == UserManager.UserId)
+ .WhereIF(filterDeviceIds != null, u => filterDeviceIds.Contains(u.DeviceId))//在指定机构列表查询
.WhereIF(sql && exportFilter.PluginType == PluginTypeEnum.Business, u => SqlFunc.JsonLike(u.VariablePropertys, exportFilter.DeviceId.ToString()))
.WhereIF(!sql && exportFilter.PluginType == PluginTypeEnum.Business && exportFilter.DeviceId > 0, u =>
@@ -566,7 +564,7 @@ internal sealed class VariableService : BaseService, IVariableService
public async Task SaveVariableAsync(Variable input, ItemChangedType type)
{
if (type == ItemChangedType.Update)
- await GlobalData.SysUserService.CheckApiDataScopeAsync(input.CreateOrgId, input.CreateUserId).ConfigureAwait(false);
+ await GlobalData.CheckByVariableId(input.Id).ConfigureAwait(false);
else
ManageHelper.CheckVariableCount(1);
@@ -767,6 +765,13 @@ internal sealed class VariableService : BaseService, IVariableService
public ImportPreviewOutput> SetVariableData(HashSet? dataScope, IReadOnlyDictionary deviceDicts, Dictionary ImportPreviews, ImportPreviewOutput> deviceImportPreview, Dictionary driverPluginNameDict, NonBlockingDictionary, Dictionary)> propertysDict, string sheetName, IEnumerable> rows)
{
+
+ List? filterDeviceIds = null;
+ if (dataScope != null)
+ {
+ filterDeviceIds = GlobalData.GetCurrentUserDeviceIds(dataScope).ToList();
+ }
+
string ImportNullError = Localizer["ImportNullError"];
string RedundantDeviceError = Localizer["RedundantDeviceError"];
@@ -839,17 +844,14 @@ internal sealed class VariableService : BaseService, IVariableService
if (GlobalData.IdDevices.TryGetValue(variable.DeviceId, out var dbvar1s) && dbvar1s.VariableRuntimes.TryGetValue(variable.Name, out var dbvar1))
{
variable.Id = dbvar1.Id;
- variable.CreateOrgId = dbvar1.CreateOrgId;
- variable.CreateUserId = dbvar1.CreateUserId;
variable.IsUp = true;
}
else
{
variable.IsUp = false;
- variable.CreateOrgId = UserManager.OrgId;
- variable.CreateUserId = UserManager.UserId;
}
- if (device.IsUp && ((dataScope != null && dataScope?.Count > 0 && !dataScope.Contains(variable.CreateOrgId)) || dataScope?.Count == 0 && variable.CreateUserId != UserManager.UserId))
+
+ if (device.IsUp && (filterDeviceIds?.Contains(variable.DeviceId) != false))
{
importPreviewOutput.Results.Add(new(Interlocked.Increment(ref row), false, "Operation not permitted"));
}
diff --git a/src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/BenchmarkAsyncWaitData.cs b/src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/BenchmarkAsyncWaitData.cs
deleted file mode 100644
index 48b78c73b..000000000
--- a/src/Plugin/ThingsGateway.Foundation.Benchmark/Benchmark/BenchmarkAsyncWaitData.cs
+++ /dev/null
@@ -1,426 +0,0 @@
-// ------------------------------------------------------------------------------
-// 此代码版权(除特别声明或在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首页:https://touchsocket.net/
-// 交流QQ群:234762506
-// 感谢您的下载和使用
-// ------------------------------------------------------------------------------
-
-using BenchmarkDotNet.Attributes;
-
-using System.Collections.Concurrent;
-using System.Runtime.CompilerServices;
-using System.Threading.Tasks.Sources;
-
-using TouchSocket.Core;
-
-namespace BenchmarkConsoleApp;
-
-
-[MemoryDiagnoser]
-public class BenchmarkAsyncWaitData
-{
- private int Count = 100000;
-
- [Benchmark]
- public async Task RunAsyncWaitDataPool()
- {
- var waitHandlePool = new WaitHandlePool();
- var cts = new CancellationTokenSource(1000 * 60);
- for (var i = 0; i < this.Count; i++)
- {
- var data = new MyWaitData();
- using (var waitData = waitHandlePool.GetWaitDataAsync(data))
- {
- var task = Task.Run(() =>
- {
- waitHandlePool.Set(data);
- });
-
-
- await waitData.WaitAsync(cts.Token).ConfigureAwait(false);
-
- await task;
- }
- }
-
- }
-
- [Benchmark]
- public async Task RunAsyncWaitData()
- {
- var waitHandlePool = new WaitHandlePool2();
- var cts = new CancellationTokenSource(1000 * 60);
- for (var i = 0; i < this.Count; i++)
- {
- var data = new MyWaitData();
- using (var waitData = waitHandlePool.GetWaitDataAsync(data))
- {
- var task = Task.Run(() =>
- {
- waitHandlePool.Set(data);
- });
-
-
- await waitData.WaitAsync(cts.Token).ConfigureAwait(false);
-
- await task;
- }
- }
- }
-
- [Benchmark]
- public async Task RunAsyncWaitDataDelayPool()
- {
- var waitHandlePool = new WaitHandlePool();
- var cts = new CancellationTokenSource(1000 * 60);
- for (var i = 0; i < this.Count; i++)
- {
- var data = new MyWaitData();
- using (var waitData = waitHandlePool.GetWaitDataAsync(data))
- {
- var task = waitData.WaitAsync(cts.Token).ConfigureAwait(false);
-
- waitData.Set(data);
-
- await task;
- }
- }
-
- }
-
- [Benchmark]
- public async Task RunAsyncWaitDataDelay()
- {
- var waitHandlePool = new WaitHandlePool2();
- var cts = new CancellationTokenSource(1000 * 60);
- for (var i = 0; i < this.Count; i++)
- {
- var data = new MyWaitData();
- using (var waitData = waitHandlePool.GetWaitDataAsync(data))
- {
- var task = waitData.WaitAsync(cts.Token).ConfigureAwait(false);
-
- waitData.Set(data);
-
- await task;
- }
- }
-
- }
-
- private class MyWaitData : IWaitHandle
- {
- public int Sign { get; set; }
- }
-
- public sealed class WaitHandlePool2
- where T : class, IWaitHandle
- {
- private readonly int m_maxSign;
- private readonly int m_minSign;
- private readonly ConcurrentDictionary> m_waitDic = new();
- private readonly Action _remove;
- private int m_currentSign;
-
- ///
- /// 初始化类的新实例。
- ///
- /// 签名的最小值,默认为1。
- /// 签名的最大值,默认为。
- ///
- /// 签名范围用于控制自动生成的唯一标识符的取值范围。
- /// 当签名达到最大值时,会自动重置到最小值重新开始分配。
- ///
- public WaitHandlePool2(int minSign = 1, int maxSign = int.MaxValue)
- {
- this.m_minSign = minSign;
- this.m_currentSign = minSign;
- this.m_maxSign = maxSign;
-
- this._remove = this.Remove;
- }
-
- ///
- /// 取消池中所有等待操作。
- ///
- ///
- /// 此方法会遍历池中所有的等待数据,并调用其方法来取消等待。
- /// 取消后的等待数据会从池中移除。适用于应用程序关闭或需要批量取消所有等待操作的场景。
- ///
- public void CancelAll()
- {
- var signs = this.m_waitDic.Keys.ToList();
- foreach (var sign in signs)
- {
- if (this.m_waitDic.TryRemove(sign, out var item))
- {
- item.Cancel();
- }
- }
- }
-
- ///
- /// 获取与指定结果关联的异步等待数据。
- ///
- /// 要关联的结果对象。
- /// 指示是否自动为结果对象分配签名,默认为。
- /// 创建的实例。
- /// 当指定的签名已被使用时抛出。
- ///
- /// 如果为,方法会自动为结果对象生成唯一签名。
- /// 创建的等待数据会被添加到池中,直到被设置结果或取消时才会移除。
- ///
- public AsyncWaitData2 GetWaitDataAsync(T result, bool autoSign = true)
- {
- if (autoSign)
- {
- result.Sign = this.GetSign();
- }
- var waitDataAsyncSlim = new AsyncWaitData2(result.Sign, this._remove, result);
-
- if (!this.m_waitDic.TryAdd(result.Sign, waitDataAsyncSlim))
- {
- //ThrowHelper.ThrowInvalidOperationException($"The sign '{result.Sign}' is already in use.");
- return default;
- }
- return waitDataAsyncSlim;
- }
-
- ///
- /// 获取具有自动生成签名的异步等待数据。
- ///
- /// 输出参数,返回自动生成的签名值。
- /// 创建的实例。
- /// 当生成的签名已被使用时抛出。
- ///
- /// 此方法会自动生成唯一签名,并创建不包含挂起数据的等待对象。
- /// 适用于只需要等待通知而不关心具体数据内容的场景。
- ///
- public AsyncWaitData2 GetWaitDataAsync(out int sign)
- {
- sign = this.GetSign();
- var waitDataAsyncSlim = new AsyncWaitData2(sign, this._remove, default);
- if (!this.m_waitDic.TryAdd(sign, waitDataAsyncSlim))
- {
- return default;
- }
- return waitDataAsyncSlim;
- }
-
- ///
- /// 使用指定结果设置对应签名的等待操作。
- ///
- /// 包含签名和结果数据的对象。
- /// 如果成功设置等待操作则返回;否则返回。
- ///
- /// 此方法根据结果对象的签名查找对应的等待数据,并设置其结果。
- /// 设置成功后,等待数据会从池中移除,正在等待的任务会被完成。
- /// 如果找不到对应签名的等待数据,则返回。
- ///
- public bool Set(T result)
- {
- if (this.m_waitDic.TryRemove(result.Sign, out var waitDataAsync))
- {
- waitDataAsync.Set(result);
- return true;
- }
- return false;
- }
-
- ///
- /// 尝试获取指定签名的异步等待数据。
- ///
- /// 要查找的签名。
- /// 输出参数,如果找到则返回对应的等待数据;否则为。
- /// 如果找到指定签名的等待数据则返回;否则返回。
- ///
- /// 此方法允许查询池中是否存在特定签名的等待数据,而不会修改池的状态。
- /// 适用于需要检查等待状态或获取等待数据进行进一步操作的场景。
- ///
- public bool TryGetDataAsync(int sign, out AsyncWaitData2 waitDataAsync)
- {
- return this.m_waitDic.TryGetValue(sign, out waitDataAsync);
- }
-
- ///
- /// 生成下一个可用的唯一签名。
- ///
- /// 生成的唯一签名值。
- ///
- /// 使用原子递增操作确保签名的唯一性和线程安全性。
- /// 当签名达到最大值时,会重新开始分配以避免溢出。
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private int GetSign()
- {
- while (true)
- {
- var currentSign = this.m_currentSign;
- var nextSign = currentSign >= this.m_maxSign ? this.m_minSign : currentSign + 1;
-
- if (Interlocked.CompareExchange(ref this.m_currentSign, nextSign, currentSign) == currentSign)
- {
- return nextSign;
- }
- // 如果CAS失败,继续重试
- }
- }
-
- ///
- /// 从池中移除指定签名的等待数据。
- ///
- /// 要移除的签名。
- ///
- /// 此方法由等待数据在释放时自动调用,确保池中不会保留已完成或已取消的等待对象。
- ///
- private void Remove(int sign)
- {
- this.m_waitDic.TryRemove(sign, out _);
- }
- }
-
- public sealed class AsyncWaitData2 : DisposableObject, IValueTaskSource
- {
- // ManualResetValueTaskSourceCore 是一个结构体,避免了额外托管对象分配,但需要配合 token 使用。
- private ManualResetValueTaskSourceCore _core; // 核心结构体,不会分配额外对象
-
- // 缓存的移除回调,由 WaitHandlePool 构造时传入,避免每次分配委托。
- private readonly Action _remove;
-
- // 挂起时的临时数据
- private readonly T _pendingData;
-
- // 完成时的数据
- private T _completedData;
-
- // 当前等待状态(成功/取消/未完成等)
- private WaitDataStatus _status;
- private CancellationTokenRegistration Registration;
-
- ///
- /// 使用指定签名和移除回调初始化一个新的 实例。
- ///
- /// 此等待项对应的签名(用于在池中查找)。
- /// 完成或释放时调用的回调,用于将此实例从等待池中移除。
- /// 可选的挂起数据,当创建时可以携带一个初始占位数据。
- public AsyncWaitData2(int sign, Action remove, T pendingData)
- {
- this.Sign = sign;
- this._remove = remove;
- this._pendingData = pendingData;
- this._core.RunContinuationsAsynchronously = true; // 确保续体异步执行,避免潜在的栈内联执行问题
- }
-
- ///
- /// 获取此等待项的签名标识。
- ///
- public int Sign { get; }
-
- ///
- /// 获取挂起时的原始数据(如果在创建时传入)。
- ///
- public T PendingData => this._pendingData;
-
- ///
- /// 获取已完成时的返回数据。
- ///
- public T CompletedData => this._completedData;
-
- ///
- /// 获取当前等待状态(例如:Success、Canceled 等)。
- ///
- public WaitDataStatus Status => this._status;
-
- ///
- /// 取消当前等待,标记为已取消并触发等待任务的异常(OperationCanceledException)。
- ///
- public void Cancel()
- {
- this.Set(WaitDataStatus.Canceled, default!);
- }
-
- ///
- /// 将等待项设置为成功并携带结果数据。
- ///
- /// 要设置的完成数据。
- public void Set(T result)
- {
- this.Set(WaitDataStatus.Success, result);
- }
-
- ///
- /// 设置等待项的状态和数据,并完成对应的 ValueTask。
- ///
- /// 要设置的状态。
- /// 要设置的完成数据。
- public void Set(WaitDataStatus status, T result)
- {
- this._status = status;
- this._completedData = result;
-
- if (status == WaitDataStatus.Canceled)
- this._core.SetException(new OperationCanceledException());
- else
- this._core.SetResult(result);
- }
-
- ///
- /// 异步等待此项完成,返回一个 ,可传入取消令牌以取消等待。
- ///
- /// 可选的取消令牌。若触发则会调用 。
- /// 表示等待状态的 ValueTask。
- public ValueTask WaitAsync(CancellationToken cancellationToken = default)
- {
- if (cancellationToken.CanBeCanceled)
- {
- this.Registration = cancellationToken.Register(this.Cancel);
- }
-
- return new ValueTask(this, this._core.Version);
- }
-
- ///
- /// 从核心获取结果(显式接口实现)。
- /// 注意:此方法由 ValueTask 基础设施调用,不应直接在用户代码中调用。
- ///
- WaitDataStatus IValueTaskSource.GetResult(short token)
- {
- this._core.GetResult(token);
- return this._status;
- }
-
- ///
- /// 获取当前 ValueTask 源的状态(显式接口实现)。
- ///
- ValueTaskSourceStatus IValueTaskSource.GetStatus(short token)
- => this._core.GetStatus(token);
-
- ///
- /// 注册续体(显式接口实现)。
- /// 注意:flags 可以控制是否捕获上下文等行为。
- ///
- void IValueTaskSource.OnCompleted(Action