Compare commits

...

11 Commits

Author SHA1 Message Date
2248356998 qq.com
00c24d06a3 chore: 更新TouchSocket依赖 2025-10-17 00:50:36 +08:00
2248356998 qq.com
3461f34240 feat: 网关监控页面树节点js更新状态
perf: 优化变量页面刷新性能
perf: 优化热点方法异步性能
2025-10-17 00:41:47 +08:00
2248356998 qq.com
aa1ce08c02 perf: 优化变量页面刷新性能 2025-10-16 18:15:42 +08:00
2248356998 qq.com
9c230c2da9 chore: ui json显示不缩进 2025-10-16 17:39:57 +08:00
2248356998 qq.com
21215d0379 build: 10.11.109
feat: js刷新变量表数据
chore: 更新BootstrapBlazor.Chart依赖
2025-10-16 16:16:07 +08:00
Diego
7448183791 !78 feat: js刷新变量表数据
* 调整列宽
* feat: js刷新变量表数据
2025-10-16 08:09:29 +00:00
Diego
35edd7dc43 !77 update src/Admin/ThingsGateway.Common/Common/ConcurrentList.cs.
Merge pull request !77 from Sunny/v10
2025-10-16 06:14:04 +00:00
Sunny
bd178831e3 update src/Admin/ThingsGateway.Common/Common/ConcurrentList.cs.
修改 ConcurrentList.cs 一处错误代码

Signed-off-by: Sunny <yhuse@163.com>
2025-10-16 04:36:31 +00:00
2248356998 qq.com
fe9ec6ad10 fix: s7连接错误 2025-10-16 12:00:08 +08:00
2248356998 qq.com
6f9ec2e24b feat: 并发字典替换为 NonBlockingDictionary 类型 2025-10-15 17:40:33 +08:00
2248356998 qq.com
c0337e2b19 build: 10.11.105
feat: json添加AllowNamedFloatingPointLiterals
2025-10-15 11:16:18 +08:00
149 changed files with 5970 additions and 628 deletions

View File

@@ -251,11 +251,13 @@ public class RequestAuditFilter : IAsyncActionFilter, IOrderedFilter
if (exception == null)
{
logger.Log(LogLevel.Information, $"{logData.Method}:{logData.Path}-{logData.Operation}");
if (logger.IsEnabled(LogLevel.Information))
logger.Log(LogLevel.Information, $"{logData.Method}:{logData.Path}-{logData.Operation}");
}
else
{
logger.Log(LogLevel.Warning, $"{logData.Method}:{logData.Path}-{logData.Operation}{Environment.NewLine}{logData.Exception?.ToSystemTextJsonString()}{Environment.NewLine}{logData.Validation?.ToSystemTextJsonString()}");
if (logger.IsEnabled(LogLevel.Warning))
logger.Log(LogLevel.Warning, $"{logData.Method}:{logData.Path}-{logData.Operation}{Environment.NewLine}{logData.Exception?.ToSystemTextJsonString()}{Environment.NewLine}{logData.Validation?.ToSystemTextJsonString()}");
}
}

View File

@@ -20,7 +20,7 @@ namespace ThingsGateway.Admin.Application;
/// <typeparam name="TEntry"></typeparam>
public class EventService<TEntry> : IEventService<TEntry>, IDisposable
{
private ConcurrentDictionary<string, Func<TEntry, Task>> Cache = new();
private NonBlockingDictionary<string, Func<TEntry, Task>> Cache = new();
public void Dispose()
{

View File

@@ -4,8 +4,8 @@
<div class="tg-table h-100">
<Table TItem="TItem" IsBordered="true" IsStriped="true" TableSize="TableSize.Compact" SelectedRows=SelectedRows SelectedRowsChanged=privateSelectedRowsChanged IsMultipleSelect="IsMultipleSelect" @ref="Instance" SearchTemplate="SearchTemplate"
DataService="DataService" CreateItemCallback="CreateItemCallback!" RenderMode=RenderMode
<Table Id=@Id TItem="TItem" IsBordered="true" IsStriped="true" TableSize="TableSize.Compact" SelectedRows=SelectedRows SelectedRowsChanged=privateSelectedRowsChanged IsMultipleSelect="IsMultipleSelect" @ref="Instance" SearchTemplate="SearchTemplate"
DataService="DataService" CreateItemCallback="CreateItemCallback!" RenderMode=RenderMode OnColumnCreating=OnColumnCreating
IsPagination="IsPagination" PageItemsSource="PageItemsSource" IsFixedHeader="IsFixedHeader" IndentSize=24 RowHeight=RowHeight ShowSearchText="ShowSearchText" ShowSearchButton="ShowSearchButton" DisableEditButtonCallback="DisableEditButtonCallback" DisableDeleteButtonCallback="DisableDeleteButtonCallback" BeforeShowEditDialogCallback=" BeforeShowEditDialogCallback!"
IsTree="IsTree" OnTreeExpand="OnTreeExpand!" TreeNodeConverter="TreeNodeConverter!" TreeIcon="fa-solid fa-circle-chevron-right" TreeExpandIcon="fa-solid fa-circle-chevron-right fa-rotate-90" IsAutoQueryFirstRender=IsAutoQueryFirstRender
ShowDefaultButtons="ShowDefaultButtons" ShowAdvancedSearch="ShowAdvancedSearch" ShowResetButton=ShowResetButton
@@ -14,7 +14,7 @@
ShowSkeleton="true" ShowLoading="ShowLoading" ShowSearch="ShowSearch" SearchModel=@SearchModel ShowLineNo
SearchMode=SearchMode ShowExportPdfButton=ShowExportPdfButton ExportButtonText=@ExportButtonText
ShowExportButton=@ShowExportButton Items=Items ClickToSelect=ClickToSelect ScrollMode=ScrollMode
ShowExportCsvButton=@ShowExportCsvButton ShowCardView=ShowCardView
ShowExportCsvButton=@ShowExportCsvButton ShowCardView=ShowCardView OnColumnVisibleChanged=OnColumnVisibleChanged
FixedExtendButtonsColumn=FixedExtendButtonsColumn FixedMultipleColumn=FixedMultipleColumn FixedDetailRowHeaderColumn=FixedDetailRowHeaderColumn FixedLineNoColumn=FixedLineNoColumn
IsAutoRefresh=IsAutoRefresh AutoRefreshInterval=AutoRefreshInterval
AllowDragColumn=@AllowDragColumn Height=@Height ShowRefresh=ShowRefresh

View File

@@ -13,13 +13,22 @@ namespace ThingsGateway.Admin.Razor;
[CascadingTypeParameter(nameof(TItem))]
public partial class AdminTable<TItem> where TItem : class, new()
{
/// <inheritdoc cref="Table{TItem}.OnColumnVisibleChanged"/>
[Parameter]
public Func<string,bool, Task> OnColumnVisibleChanged { get; set; }
/// <inheritdoc cref="Table{TItem}.OnColumnCreating"/>
[Parameter]
public Func<List<ITableColumn>,Task> OnColumnCreating { get; set; }
/// <inheritdoc cref="Table{TItem}.RenderMode"/>
[Parameter]
public TableRenderMode RenderMode { get; set; }
public List<ITableColumn> Columns => Instance?.Columns;
public IEnumerable<ITableColumn> GetVisibleColumns => Instance?.GetVisibleColumns();
public List<TItem> Rows => Instance?.Rows;
/// <inheritdoc cref="Table{TItem}.SelectedRowsChanged"/>
[Parameter]
@@ -158,6 +167,9 @@ public partial class AdminTable<TItem> where TItem : class, new()
[Parameter]
public IDataService<TItem> DataService { get; set; }
[Parameter]
public string? Id { get; set; }
/// <inheritdoc cref="Table{TItem}.CreateItemCallback"/>
[Parameter]
public Func<TItem> CreateItemCallback { get; set; }

View File

@@ -72,7 +72,7 @@ public partial class HardwareInfoPage : IDisposable
ChartDataSource.Options.Title = Localizer[nameof(HistoryHardwareInfo)];
ChartDataSource.Options.X.Title = Localizer["DateTime"];
ChartDataSource.Options.Y.Title = Localizer["Data"];
ChartDataSource.Labels = hisHardwareInfos.Select(a => a.Date.ToString("dd HH:mm zz"));
ChartDataSource.Labels = hisHardwareInfos.Select(a => a.Date.ToString("dd-HH:mm"));
ChartDataSource.Data.Add(new ChartDataset()
{
Tension = 0.4f,
@@ -116,7 +116,7 @@ public partial class HardwareInfoPage : IDisposable
else
{
var hisHardwareInfos = await HardwareJob.GetHistoryHardwareInfos();
ChartDataSource.Labels = hisHardwareInfos.Select(a => a.Date.ToString("dd HH:mm zz"));
ChartDataSource.Labels = hisHardwareInfos.Select(a => a.Date.ToString("dd-HH:mm"));
ChartDataSource.Data[0].Data = hisHardwareInfos.Select(a => (object)a.CpuUsage);
ChartDataSource.Data[1].Data = hisHardwareInfos.Select(a => (object)a.MemoryUsage);
ChartDataSource.Data[2].Data = hisHardwareInfos.Select(a => (object)a.DriveUsage);

View File

@@ -5,7 +5,7 @@
<ItemGroup>
<ProjectReference Include="..\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj" />
<PackageReference Include="BootstrapBlazor.Chart" Version="9.0.3" />
<PackageReference Include="BootstrapBlazor.Chart" Version="9.0.4" />
<PackageReference Include="BootstrapBlazor.UniverSheet" Version="9.0.5" />
<PackageReference Include="BootstrapBlazor.WinBox" Version="9.0.7" />
<PackageReference Include="BootstrapBlazor.CodeEditor" Version="9.0.3" />

View File

@@ -1,4 +1,4 @@
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
@@ -477,7 +477,7 @@ public class ConcurrentList<T> : IList<T>, IReadOnlyList<T>
{
lock (((ICollection)m_list).SyncRoot)
{
return m_list.IndexOf(item);
return m_list.LastIndexOf(item);
}
}

View File

@@ -82,7 +82,7 @@ public static class ObjectExtensions
/// <typeparam name="T"></typeparam>
/// <param name="dic">字典</param>
/// <param name="newDic">新字典</param>
internal static void AddOrUpdate<T>(this ConcurrentDictionary<string, T> dic, Dictionary<string, T> newDic)
internal static void AddOrUpdate<T>(this NonBlockingDictionary<string, T> dic, Dictionary<string, T> newDic)
{
foreach (var (key, value) in newDic)
{

View File

@@ -66,7 +66,8 @@ internal class JsonStringLocalizer(Assembly assembly, string typeName, string ba
}
catch (Exception ex)
{
Logger.LogError(ex, "{JsonStringLocalizerName} searched for '{Name}' in '{typeName}' with culture '{CultureName}' throw exception.", nameof(JsonStringLocalizer), name, typeName, CultureInfo.CurrentUICulture.Name);
if (Logger?.IsEnabled(LogLevel.Error) == true)
Logger.LogError(ex, "{JsonStringLocalizerName} searched for '{Name}' in '{typeName}' with culture '{CultureName}' throw exception.", nameof(JsonStringLocalizer), name, typeName, CultureInfo.CurrentUICulture.Name);
}
return ret;
}
@@ -176,7 +177,8 @@ internal class JsonStringLocalizer(Assembly assembly, string typeName, string ba
localizationMissingItemHandler.HandleMissingItem(name, typeName, CultureInfo.CurrentUICulture.Name);
if (jsonLocalizationOptions.IgnoreLocalizerMissing == false)
{
Logger.LogInformation("{JsonStringLocalizerName} searched for '{Name}' in '{TypeName}' with culture '{CultureName}' not found.", nameof(JsonStringLocalizer), name, typeName, CultureInfo.CurrentUICulture.Name);
if (Logger?.IsEnabled(LogLevel.Information) == true)
Logger.LogInformation("{JsonStringLocalizerName} searched for '{Name}' in '{TypeName}' with culture '{CultureName}' not found.", nameof(JsonStringLocalizer), name, typeName, CultureInfo.CurrentUICulture.Name);
}
_missingManifestCache.TryAdd($"name={name}&culture={CultureInfo.CurrentUICulture.Name}");
}

View File

@@ -205,7 +205,7 @@ public static class ObjectExtensions
/// <typeparam name="T"></typeparam>
/// <param name="dic">字典</param>
/// <param name="newDic">新字典</param>
internal static void AddOrUpdate<T>(this ConcurrentDictionary<string, T> dic, Dictionary<string, T> newDic)
internal static void AddOrUpdate<T>(this NonBlockingDictionary<string, T> dic, Dictionary<string, T> newDic)
{
foreach (var (key, value) in newDic)
{

View File

@@ -94,7 +94,7 @@ public static class AspNetCoreBuilderServiceCollectionExtensions
/// <param name="mvcBuilder"></param>
/// <param name="configure"></param>
/// <returns></returns>
public static IMvcBuilder AddFromConvertBinding(this IMvcBuilder mvcBuilder, Action<ConcurrentDictionary<Type, Type>> configure = default)
public static IMvcBuilder AddFromConvertBinding(this IMvcBuilder mvcBuilder, Action<NonBlockingDictionary<Type, Type>> configure = default)
{
mvcBuilder.Services.AddFromConvertBinding(configure);
@@ -107,13 +107,13 @@ public static class AspNetCoreBuilderServiceCollectionExtensions
/// <param name="services"></param>
/// <param name="configure"></param>
/// <returns></returns>
public static IServiceCollection AddFromConvertBinding(this IServiceCollection services, Action<ConcurrentDictionary<Type, Type>> configure = default)
public static IServiceCollection AddFromConvertBinding(this IServiceCollection services, Action<NonBlockingDictionary<Type, Type>> configure = default)
{
// 非 Web 环境跳过注册
if (App.WebHostEnvironment == default) return services;
// 定义模型绑定转换器集合
var modelBinderConverts = new ConcurrentDictionary<Type, Type>();
var modelBinderConverts = new NonBlockingDictionary<Type, Type>();
modelBinderConverts.TryAdd(typeof(DateTime), typeof(DateTimeModelConvertBinder));
modelBinderConverts.TryAdd(typeof(DateTimeOffset), typeof(DateTimeOffsetModelConvertBinder));

View File

@@ -27,13 +27,13 @@ public class FromConvertBinder : IModelBinder
/// <summary>
/// 定义模型绑定转换器集合
/// </summary>
private readonly ConcurrentDictionary<Type, Type> _modelBinderConverts;
private readonly NonBlockingDictionary<Type, Type> _modelBinderConverts;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="modelBinderConverts">定义模型绑定转换器集合</param>
public FromConvertBinder(ConcurrentDictionary<Type, Type> modelBinderConverts)
public FromConvertBinder(NonBlockingDictionary<Type, Type> modelBinderConverts)
{
_modelBinderConverts = modelBinderConverts;
}

View File

@@ -28,13 +28,13 @@ public class FromConvertBinderProvider : IModelBinderProvider
/// <summary>
/// 定义模型绑定转换器集合
/// </summary>
private readonly ConcurrentDictionary<Type, Type> _modelBinderConverts;
private readonly NonBlockingDictionary<Type, Type> _modelBinderConverts;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="modelBinderConverts">定义模型绑定转换器集合</param>
public FromConvertBinderProvider(ConcurrentDictionary<Type, Type> modelBinderConverts)
public FromConvertBinderProvider(NonBlockingDictionary<Type, Type> modelBinderConverts)
{
_modelBinderConverts = modelBinderConverts;
}

View File

@@ -40,7 +40,7 @@ public static class DataValidator
/// <summary>
/// 验证类型正则表达式
/// </summary>
private static readonly ConcurrentDictionary<string, ValidationItemMetadataAttribute> ValidationItemMetadatas;
private static readonly NonBlockingDictionary<string, ValidationItemMetadataAttribute> ValidationItemMetadatas;
/// <summary>
/// 构造函数
@@ -57,7 +57,7 @@ public static class DataValidator
ValidationItemMetadatas = GetValidationValidationItemMetadatas();
// 缓存所有正则表达式
GetValidationTypeValidationItemMetadataCached = new ConcurrentDictionary<object, (string, ValidationItemMetadataAttribute)>();
GetValidationTypeValidationItemMetadataCached = new NonBlockingDictionary<object, (string, ValidationItemMetadataAttribute)>();
}
/// <summary>
@@ -203,7 +203,7 @@ public static class DataValidator
/// <summary>
/// 获取验证类型验证Item集合
/// </summary>
private static readonly ConcurrentDictionary<object, (string, ValidationItemMetadataAttribute)> GetValidationTypeValidationItemMetadataCached;
private static readonly NonBlockingDictionary<object, (string, ValidationItemMetadataAttribute)> GetValidationTypeValidationItemMetadataCached;
/// <summary>
/// 获取验证类型正则表达式(需要缓存)
@@ -267,9 +267,9 @@ public static class DataValidator
/// 获取验证类型所有有效的正则表达式
/// </summary>
/// <returns></returns>
private static ConcurrentDictionary<string, ValidationItemMetadataAttribute> GetValidationValidationItemMetadatas()
private static NonBlockingDictionary<string, ValidationItemMetadataAttribute> GetValidationValidationItemMetadatas()
{
var vaidationItems = new ConcurrentDictionary<string, ValidationItemMetadataAttribute>();
var vaidationItems = new NonBlockingDictionary<string, ValidationItemMetadataAttribute>();
// 查找所有 [ValidationMessageType] 类型中的 [ValidationMessage] 消息定义
var customErrorMessages = ValidationMessageTypes.SelectMany(u => u.GetFields()

View File

@@ -353,7 +353,7 @@ public static class DependencyInjectionServiceCollectionExtensions
/// <summary>
/// 类型名称集合
/// </summary>
private static readonly ConcurrentDictionary<string, Type> TypeNamedCollection;
private static readonly NonBlockingDictionary<string, Type> TypeNamedCollection;
/// <summary>
/// 创建代理方法
@@ -374,7 +374,7 @@ public static class DependencyInjectionServiceCollectionExtensions
GlobalServiceProxyType = App.EffectiveTypes
.FirstOrDefault(u => typeof(AspectDispatchProxy).IsAssignableFrom(u) && typeof(IGlobalDispatchProxy).IsAssignableFrom(u) && u.IsClass && !u.IsInterface && !u.IsAbstract);
TypeNamedCollection = new ConcurrentDictionary<string, Type>();
TypeNamedCollection = new NonBlockingDictionary<string, Type>();
DispatchCreateMethod = typeof(AspectDispatchProxy).GetMethod(nameof(AspectDispatchProxy.Create));
}
}

View File

@@ -28,21 +28,21 @@ internal static class Penetrates
/// <summary>
/// 请求动词映射字典
/// </summary>
internal static ConcurrentDictionary<string, string> VerbToHttpMethods { get; private set; }
internal static NonBlockingDictionary<string, string> VerbToHttpMethods { get; private set; }
/// <summary>
/// 控制器排序集合
/// </summary>
internal static ConcurrentDictionary<string, (string, int, Type)> ControllerOrderCollection { get; set; }
internal static NonBlockingDictionary<string, (string, int, Type)> ControllerOrderCollection { get; set; }
/// <summary>
/// 构造函数
/// </summary>
static Penetrates()
{
ControllerOrderCollection = new ConcurrentDictionary<string, (string, int, Type)>();
ControllerOrderCollection = new NonBlockingDictionary<string, (string, int, Type)>();
VerbToHttpMethods = new ConcurrentDictionary<string, string>
VerbToHttpMethods = new NonBlockingDictionary<string, string>
{
["post"] = "POST",
["add"] = "POST",
@@ -67,13 +67,13 @@ internal static class Penetrates
["patch"] = "PATCH"
};
//IsApiControllerCached = new ConcurrentDictionary<Type, bool>();
//IsApiControllerCached = new NonBlockingDictionary<Type, bool>();
}
///// <summary>
///// <see cref="IsApiController(Type)"/> 缓存集合
///// </summary>
//private static readonly ConcurrentDictionary<Type, bool> IsApiControllerCached;
//private static readonly NonBlockingDictionary<Type, bool> IsApiControllerCached;
/// <summary>
/// 是否是Api控制器

View File

@@ -61,7 +61,7 @@ internal sealed class EventBusHostedService : BackgroundService
/// <summary>
/// 事件处理程序集合
/// </summary>
private readonly ConcurrentDictionary<EventHandlerWrapper, EventHandlerWrapper> _eventHandlers = new();
private readonly NonBlockingDictionary<EventHandlerWrapper, EventHandlerWrapper> _eventHandlers = new();
/// <summary>
/// 构造函数
@@ -295,7 +295,8 @@ internal sealed class EventBusHostedService : BackgroundService
, retryAction: (total, times) =>
{
// 输出重试日志
_logger.LogWarning("Retrying {times}/{total} times for {EventId}", times, total, eventSource.EventId);
if (_logger?.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Warning) == true)
_logger.LogWarning("Retrying {times}/{total} times for {EventId}", times, total, eventSource.EventId);
}).ConfigureAwait(false);
}
else

View File

@@ -31,7 +31,7 @@ public static class Oops
/// <summary>
/// 方法错误异常特性
/// </summary>
private static readonly ConcurrentDictionary<MethodBase, MethodIfException> _errorMethods;
private static readonly NonBlockingDictionary<MethodBase, MethodIfException> _errorMethods;
/// <summary>
/// 错误代码类型
@@ -41,7 +41,7 @@ public static class Oops
/// <summary>
/// 错误消息字典
/// </summary>
private static readonly ConcurrentDictionary<string, string> _errorCodeMessages;
private static readonly NonBlockingDictionary<string, string> _errorCodeMessages;
/// <summary>
/// 友好异常设置
@@ -53,7 +53,7 @@ public static class Oops
/// </summary>
static Oops()
{
_errorMethods = new ConcurrentDictionary<MethodBase, MethodIfException>();
_errorMethods = new NonBlockingDictionary<MethodBase, MethodIfException>();
_friendlyExceptionSettings = App.GetConfig<FriendlyExceptionSettingsOptions>("FriendlyExceptionSettings", true);
_errorCodeTypes = GetErrorCodeTypes();
_errorCodeMessages = GetErrorCodeMessages();
@@ -258,9 +258,9 @@ public static class Oops
/// 获取所有错误消息
/// </summary>
/// <returns></returns>
private static ConcurrentDictionary<string, string> GetErrorCodeMessages()
private static NonBlockingDictionary<string, string> GetErrorCodeMessages()
{
var defaultErrorCodeMessages = new ConcurrentDictionary<string, string>();
var defaultErrorCodeMessages = new NonBlockingDictionary<string, string>();
// 查找所有 [ErrorCodeType] 类型中的 [ErrorCodeMetadata] 元数据定义
var errorCodeMessages = _errorCodeTypes.SelectMany(u => u.GetFields().Where(u => u.IsDefined(typeof(ErrorCodeItemMetadataAttribute))))

View File

@@ -23,7 +23,7 @@ public static class ILoggerExtensions
/// 设置日志上下文
/// </summary>
/// <param name="logger"></param>
/// <param name="properties">建议使用 ConcurrentDictionary 类型</param>
/// <param name="properties">建议使用 NonBlockingDictionary 类型</param>
/// <returns></returns>
public static IDisposable ScopeContext(this ILogger logger, IDictionary<string, object> properties)
{

View File

@@ -82,7 +82,7 @@ public static class StringLoggingExtensions
/// 配置日志上下文
/// </summary>
/// <param name="message"></param>
/// <param name="properties">建议使用 ConcurrentDictionary 类型</param>
/// <param name="properties">建议使用 NonBlockingDictionary 类型</param>
/// <returns></returns>
public static StringLoggingPart ScopeContext(this string message, IDictionary<string, object> properties)
{

View File

@@ -44,7 +44,7 @@ public sealed class FileLoggerProvider : ILoggerProvider, ISupportExternalScope
/// 记录日志所有滚动文件名
/// </summary>
/// <remarks>只有 MaxRollingFiles 和 FileSizeLimitBytes 大于 0 有效</remarks>
internal readonly ConcurrentDictionary<string, FileInfo> _rollingFileNames = new();
internal readonly NonBlockingDictionary<string, FileInfo> _rollingFileNames = new();
/// <summary>
/// 文件日志写入器

View File

@@ -94,7 +94,7 @@ public sealed partial class StringLoggingPart
/// <summary>
/// 配置日志上下文
/// </summary>
/// <param name="properties">建议使用 ConcurrentDictionary 类型</param>
/// <param name="properties">建议使用 NonBlockingDictionary 类型</param>
/// <returns></returns>
public StringLoggingPart ScopeContext(IDictionary<string, object> properties)
{

View File

@@ -57,7 +57,7 @@ public static class Log
/// <summary>
/// 配置日志上下文
/// </summary>
/// <param name="properties">建议使用 ConcurrentDictionary 类型</param>
/// <param name="properties">建议使用 NonBlockingDictionary 类型</param>
/// <returns></returns>
public static (ILogger logger, IDisposable scope) ScopeContext(IDictionary<string, object> properties)
{

View File

@@ -21,7 +21,7 @@ internal sealed class JobCancellationToken : IJobCancellationToken
/// <summary>
/// 取消作业执行 Token 集合
/// </summary>
private readonly ConcurrentDictionary<string, CancellationTokenSource> _cancellationTokenSources;
private readonly NonBlockingDictionary<string, CancellationTokenSource> _cancellationTokenSources;
/// <summary>
/// 作业调度器日志服务

View File

@@ -167,7 +167,7 @@ public partial class JobDetail
/// <summary>
/// 带命名规则的数据库列名
/// </summary>
private readonly ConcurrentDictionary<NamingConventions, string[]> _namingColumnNames = new();
private readonly NonBlockingDictionary<NamingConventions, string[]> _namingColumnNames = new();
/// <summary>
/// 获取数据库列名

View File

@@ -65,7 +65,7 @@ internal sealed partial class SchedulerFactory : ISchedulerFactory
/// <summary>
/// 作业计划集合
/// </summary>
private readonly ConcurrentDictionary<string, Scheduler> _schedulers = new();
private readonly NonBlockingDictionary<string, Scheduler> _schedulers = new();
/// <summary>
/// 作业计划构建器集合

View File

@@ -369,11 +369,13 @@ internal sealed class ScheduleHostedService : BackgroundService
// 写入作业执行详细日志
if (executionException == null)
{
jobLogger?.LogInformation("{jobExecutingContext}", jobExecutingContext);
if (jobLogger?.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information) == true)
jobLogger?.LogInformation("{jobExecutingContext}", jobExecutingContext);
}
else
{
jobLogger?.LogError(executionException, "{jobExecutingContext}", jobExecutingContext);
if (jobLogger?.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Error) == true)
jobLogger?.LogError(executionException, "{jobExecutingContext}", jobExecutingContext);
}
// 记录作业触发器运行信息

View File

@@ -380,7 +380,7 @@ public partial class Trigger
/// <summary>
/// 带命名规则的数据库列名
/// </summary>
private readonly ConcurrentDictionary<NamingConventions, string[]> _namingColumnNames = new();
private readonly NonBlockingDictionary<NamingConventions, string[]> _namingColumnNames = new();
/// <summary>
/// 获取数据库列名

View File

@@ -83,11 +83,11 @@ public static class SpecificationDocumentBuilder
// 初始化常量
_groupOrderRegex = new Regex(@"@(?<order>[0-9]+$)");
GetActionGroupsCached = new ConcurrentDictionary<MethodInfo, IEnumerable<GroupExtraInfo>>();
GetControllerGroupsCached = new ConcurrentDictionary<Type, IEnumerable<GroupExtraInfo>>();
GetGroupOpenApiInfoCached = new ConcurrentDictionary<string, SpecificationOpenApiInfo>();
GetControllerTagCached = new ConcurrentDictionary<ControllerActionDescriptor, string>();
GetActionTagCached = new ConcurrentDictionary<ApiDescription, string>();
GetActionGroupsCached = new NonBlockingDictionary<MethodInfo, IEnumerable<GroupExtraInfo>>();
GetControllerGroupsCached = new NonBlockingDictionary<Type, IEnumerable<GroupExtraInfo>>();
GetGroupOpenApiInfoCached = new NonBlockingDictionary<string, SpecificationOpenApiInfo>();
GetControllerTagCached = new NonBlockingDictionary<ControllerActionDescriptor, string>();
GetActionTagCached = new NonBlockingDictionary<ApiDescription, string>();
// 默认分组,支持多个逗号分割
DocumentGroupExtras = new List<GroupExtraInfo> { ResolveGroupExtraInfo(_specificationDocumentSettings.DefaultGroupName) };
@@ -143,7 +143,7 @@ public static class SpecificationDocumentBuilder
/// <summary>
/// 获取分组信息缓存集合
/// </summary>
private static readonly ConcurrentDictionary<string, SpecificationOpenApiInfo> GetGroupOpenApiInfoCached;
private static readonly NonBlockingDictionary<string, SpecificationOpenApiInfo> GetGroupOpenApiInfoCached;
/// <summary>
/// 获取分组配置信息
@@ -738,7 +738,7 @@ public static class SpecificationDocumentBuilder
/// <summary>
/// 获取控制器组缓存集合
/// </summary>
private static readonly ConcurrentDictionary<Type, IEnumerable<GroupExtraInfo>> GetControllerGroupsCached;
private static readonly NonBlockingDictionary<Type, IEnumerable<GroupExtraInfo>> GetControllerGroupsCached;
/// <summary>
/// 获取控制器分组列表
@@ -773,7 +773,7 @@ public static class SpecificationDocumentBuilder
/// <summary>
/// <see cref="GetActionGroups(MethodInfo)"/> 缓存集合
/// </summary>
private static readonly ConcurrentDictionary<MethodInfo, IEnumerable<GroupExtraInfo>> GetActionGroupsCached;
private static readonly NonBlockingDictionary<MethodInfo, IEnumerable<GroupExtraInfo>> GetActionGroupsCached;
/// <summary>
/// 获取动作方法分组列表
@@ -808,7 +808,7 @@ public static class SpecificationDocumentBuilder
/// <summary>
/// <see cref="GetActionTag(ApiDescription)"/> 缓存集合
/// </summary>
private static readonly ConcurrentDictionary<ControllerActionDescriptor, string> GetControllerTagCached;
private static readonly NonBlockingDictionary<ControllerActionDescriptor, string> GetControllerTagCached;
/// <summary>
/// 获取控制器标签
@@ -835,7 +835,7 @@ public static class SpecificationDocumentBuilder
/// <summary>
/// <see cref="GetActionTag(ApiDescription)"/> 缓存集合
/// </summary>
private static readonly ConcurrentDictionary<ApiDescription, string> GetActionTagCached;
private static readonly NonBlockingDictionary<ApiDescription, string> GetActionTagCached;
/// <summary>
/// 获取动作方法标签

View File

@@ -51,12 +51,12 @@ public static class UnifyContext
/// <summary>
/// 规范化结果提供器
/// </summary>
internal static ConcurrentDictionary<string, UnifyMetadata> UnifyProviders = new();
internal static NonBlockingDictionary<string, UnifyMetadata> UnifyProviders = new();
/// <summary>
/// 规范化序列化配置
/// </summary>
internal static ConcurrentDictionary<string, object> UnifySerializerSettings = new();
internal static NonBlockingDictionary<string, object> UnifySerializerSettings = new();
/// <summary>
/// 获取异常元数据

View File

@@ -14,7 +14,7 @@ using System.Collections.Concurrent;
namespace ThingsGateway.Extension;
/// <summary>
/// <see cref="ConcurrentDictionary{TKey, TValue}" /> 拓展类
/// <see cref="NonBlockingDictionary{TKey, TValue}" /> 拓展类
/// </summary>
internal static class ConcurrentDictionaryExtensions
{
@@ -24,7 +24,7 @@ internal static class ConcurrentDictionaryExtensions
/// <typeparam name="TKey">字典键类型</typeparam>
/// <typeparam name="TValue">字典值类型</typeparam>
/// <param name="dictionary">
/// <see cref="ConcurrentDictionary{TKey, TValue}" />
/// <see cref="NonBlockingDictionary{TKey, TValue}" />
/// </param>
/// <param name="key">
/// <typeparamref name="TKey" />
@@ -36,7 +36,7 @@ internal static class ConcurrentDictionaryExtensions
/// <returns>
/// <see cref="bool" />
/// </returns>
internal static bool TryUpdate<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> dictionary
internal static bool TryUpdate<TKey, TValue>(this NonBlockingDictionary<TKey, TValue> dictionary
, TKey key
, Func<TValue, TValue> updateFactory
, out TValue? value)

View File

@@ -241,7 +241,7 @@ internal static class IDictionaryExtensions
/// </summary>
/// <remarks>其中键是由值通过给定的选择器函数生成的。</remarks>
/// <param name="dictionary">
/// <see cref="ConcurrentDictionary{TKey, TValue}" />
/// <see cref="NonBlockingDictionary{TKey, TValue}" />
/// </param>
/// <param name="values">
/// <see cref="IEnumerable{T}" />
@@ -249,7 +249,7 @@ internal static class IDictionaryExtensions
/// <param name="keySelector">键选择器</param>
/// <typeparam name="TKey">字典键类型</typeparam>
/// <typeparam name="TValue">字典值类型</typeparam>
internal static void TryAdd<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> dictionary,
internal static void TryAdd<TKey, TValue>(this NonBlockingDictionary<TKey, TValue> dictionary,
IEnumerable<TValue>? values, Func<TValue, TKey> keySelector)
where TKey : notnull
{

View File

@@ -21,20 +21,20 @@ internal sealed class CoreOptions
/// <summary>
/// 已注册的组件元数据集合
/// </summary>
internal readonly ConcurrentDictionary<string, ComponentMetadata> _metadataOfRegistered;
internal readonly NonBlockingDictionary<string, ComponentMetadata> _metadataOfRegistered;
/// <summary>
/// 子选项集合
/// </summary>
internal readonly ConcurrentDictionary<Type, object> _optionsInstances;
internal readonly NonBlockingDictionary<Type, object> _optionsInstances;
/// <summary>
/// <inheritdoc cref="CoreOptions" />
/// </summary>
internal CoreOptions()
{
_optionsInstances = new ConcurrentDictionary<Type, object>();
_metadataOfRegistered = new ConcurrentDictionary<string, ComponentMetadata>(StringComparer.OrdinalIgnoreCase);
_optionsInstances = new NonBlockingDictionary<Type, object>();
_metadataOfRegistered = new NonBlockingDictionary<string, ComponentMetadata>(StringComparer.OrdinalIgnoreCase);
EntryComponentTypes = [];
}

View File

@@ -32,7 +32,7 @@ public sealed class ObjectPropertyGetter<T> where T : class
/// <summary>
/// 对象类型实例属性值访问器集合
/// </summary>
internal readonly ConcurrentDictionary<string, Func<object, object?>> _propertyGetters = new();
internal readonly NonBlockingDictionary<string, Func<object, object?>> _propertyGetters = new();
/// <summary>
/// <inheritdoc cref="ObjectPropertyGetter{T}" />

View File

@@ -32,7 +32,7 @@ public sealed class ObjectPropertySetter<T> where T : class
/// <summary>
/// 对象类型实例属性值设置器集合
/// </summary>
internal readonly ConcurrentDictionary<string, Action<object, object?>> _propertySetters = new();
internal readonly NonBlockingDictionary<string, Action<object, object?>> _propertySetters = new();
/// <summary>
/// <inheritdoc cref="ObjectPropertySetter{T}" />

View File

@@ -27,7 +27,7 @@ public sealed class HttpDeclarativeBuilder
/// <summary>
/// HTTP 声明式 <see cref="IHttpDeclarativeExtractor" /> 提取器集合
/// </summary>
internal static readonly ConcurrentDictionary<Type, IHttpDeclarativeExtractor> _extractors = new([
internal static readonly NonBlockingDictionary<Type, IHttpDeclarativeExtractor> _extractors = new([
new(typeof(BaseAddressDeclarativeExtractor), new BaseAddressDeclarativeExtractor()),
new(typeof(ValidationDeclarativeExtractor), new ValidationDeclarativeExtractor()),
new(typeof(AutoSetHostHeaderDeclarativeExtractor), new AutoSetHostHeaderDeclarativeExtractor()),
@@ -56,7 +56,7 @@ public sealed class HttpDeclarativeBuilder
/// HTTP 声明式 <see cref="IHttpDeclarativeExtractor" /> 提取器集合(冻结)
/// </summary>
/// <remarks>该集合用于确保某些 HTTP 声明式提取器始终位于最后。</remarks>
internal static readonly ConcurrentDictionary<Type, IFrozenHttpDeclarativeExtractor> _frozenExtractors = new([
internal static readonly NonBlockingDictionary<Type, IFrozenHttpDeclarativeExtractor> _frozenExtractors = new([
new(typeof(MultipartDeclarativeExtractor), new MultipartDeclarativeExtractor()),
new(typeof(HttpMultipartFormDataBuilderDeclarativeExtractor),
new HttpMultipartFormDataBuilderDeclarativeExtractor()),

View File

@@ -251,7 +251,8 @@ public sealed class ProfilerDelegatingHandler(ILogger<Logging> logger, IOptions<
// 检查是否配置(注册)了日志程序
if (remoteOptions.IsLoggingRegistered)
{
logger.Log(remoteOptions.ProfilerLogLevel, "{message}", message);
if (logger?.IsEnabled(remoteOptions.ProfilerLogLevel) == true)
logger.Log(remoteOptions.ProfilerLogLevel, "{message}", message);
}
else
{

View File

@@ -27,7 +27,7 @@ public class MessagePackContentProcessor : HttpContentProcessorBase
/// <summary>
/// MessagePack 序列化器委托字典缓存
/// </summary>
internal static readonly ConcurrentDictionary<Type, Func<object, byte[]>> _serializerCache = new();
internal static readonly NonBlockingDictionary<Type, Func<object, byte[]>> _serializerCache = new();
/// <summary>
/// 初始化 MessagePack 序列化器委托

View File

@@ -23,7 +23,7 @@ public class MemoryCache : Cache
{
#region
/// <summary>缓存核心</summary>
protected ConcurrentDictionary<String, CacheItem> _cache = new();
protected NonBlockingDictionary<String, CacheItem> _cache = new();
/// <summary>容量。容量超标时采用LRU机制删除默认100_000</summary>
public Int32 Capacity { get; set; } = 100_000;
@@ -379,7 +379,7 @@ public class MemoryCache : Cache
/// <returns></returns>
public override IDictionary<String, T> GetDictionary<T>(String key)
{
var item = GetOrAddItem(key, k => new ConcurrentDictionary<String, T>());
var item = GetOrAddItem(key, k => new NonBlockingDictionary<String, T>());
return item.Visit<IDictionary<String, T>>() ??
throw new InvalidCastException($"Unable to convert the value of [{key}] from {item.TypeCode} to {typeof(IDictionary<String, T>)}");
}

View File

@@ -2,7 +2,7 @@
后续例程与使用说明均以Redis为例各缓存实现类似。
### 内存缓存 MemoryCache
MemoryCache核心是并发字典ConcurrentDictionary由于省去了序列化和网络通信使得它具有千万级超高性能。
MemoryCache核心是并发字典NonBlockingDictionary由于省去了序列化和网络通信使得它具有千万级超高性能。
MemoryCache支持过期时间默认容量10万个未过期key超过该值后每60秒根据LRU清理溢出部分。
常用于进程内千万级以下数据缓存场景。

View File

@@ -44,7 +44,7 @@ public static class CollectionHelper
{
//if (collection == null) return null;
if (collection is ConcurrentDictionary<TKey, TValue> cdiv && cdiv.Keys is IList<TKey> list) return list;
if (collection is NonBlockingDictionary<TKey, TValue> cdiv && cdiv.Keys is IList<TKey> list) return list;
if (collection.Count == 0) return [];
lock (collection)
@@ -65,8 +65,8 @@ public static class CollectionHelper
{
//if (collection == null) return null;
//if (collection is ConcurrentDictionary<TKey, TValue> cdiv) return cdiv.Values as IList<TValue>;
if (collection is ConcurrentDictionary<TKey, TValue> cdiv && cdiv.Values is IList<TValue> list) return list;
//if (collection is NonBlockingDictionary<TKey, TValue> cdiv) return cdiv.Values as IList<TValue>;
if (collection is NonBlockingDictionary<TKey, TValue> cdiv && cdiv.Values is IList<TValue> list) return list;
if (collection.Count == 0) return [];
lock (collection)

View File

@@ -9,7 +9,7 @@ namespace ThingsGateway.NewLife.Collections;
/// </remarks>
public class ConcurrentHashSet<T> : IEnumerable<T> where T : notnull
{
private readonly ConcurrentDictionary<T, Byte> _dic = new();
private readonly NonBlockingDictionary<T, Byte> _dic = new();
/// <summary>是否空集合</summary>
public Boolean IsEmpty => _dic.IsEmpty;

View File

@@ -0,0 +1,77 @@
// Copyright (c) Vladimir Sadov. All rights reserved.
//
// This file is distributed under the MIT License. See LICENSE.md for details.
#nullable disable
namespace System.Collections.Concurrent
{
internal abstract partial class DictionaryImpl<TKey, TKeyStore, TValue>
: DictionaryImpl<TKey, TValue>
{
internal override Snapshot GetSnapshot()
{
return new SnapshotImpl(this);
}
private class SnapshotImpl : Snapshot
{
private readonly DictionaryImpl<TKey, TKeyStore, TValue> _table;
public SnapshotImpl(DictionaryImpl<TKey, TKeyStore, TValue> dict)
{
this._table = dict;
// linearization point.
// if table is quiescent and has no copy in progress,
// we can simply iterate over its table.
while (_table._newTable != null)
{
// there is a copy in progress, finish it and try again
_table.HelpCopy(copy_all: true);
this._table = (DictionaryImpl<TKey, TKeyStore, TValue>)this._table._topDict._table;
}
}
public override int Count => _table.Count;
public override bool MoveNext()
{
var entries = this._table._entries;
while (_idx < entries.Length)
{
var nextEntry = entries[_idx++];
if (nextEntry.value != null)
{
var nextKstore = nextEntry.key;
if (nextKstore == null)
{
// slot was deleted.
continue;
}
_curKey = _table.keyFromEntry(nextKstore);
object nextV = _table.TryGetValue(_curKey);
if (nextV != null)
{
_curValue = _table.FromObjectValue(nextV);
return true;
}
}
}
_curKey = default;
_curValue = default;
return false;
}
public override void Reset()
{
_idx = 0;
_curKey = default;
_curValue = default;
}
}
}
}

View File

@@ -0,0 +1,93 @@
// Copyright (c) Vladimir Sadov. All rights reserved.
//
// This file is distributed under the MIT License. See LICENSE.md for details.
#nullable disable
using System.Runtime.CompilerServices;
namespace System.Collections.Concurrent
{
internal abstract class DictionaryImpl
{
internal enum ValueMatch
{
Any, // sets new value unconditionally, used by index set and TryRemove(key)
NullOrDead, // set value if original value is null or dead, used by Add/TryAdd
NotNullOrDead, // set value if original value is alive, used by Remove
OldValue, // sets new value if old value matches
}
internal sealed class Prime
{
internal object originalValue;
public Prime(object originalValue)
{
this.originalValue = originalValue;
}
}
internal static readonly object TOMBSTONE = new object();
internal static readonly Prime TOMBPRIME = new Prime(TOMBSTONE);
internal static readonly object NULLVALUE = new object();
// represents a trivially copied empty entry
// we insert it in the old table during rehashing
// to reduce chances that more entries are added
protected const int TOMBPRIMEHASH = 1 << 31;
// we cannot distigush zero keys from uninitialized state
// so we force them to have this special hash instead
protected const int ZEROHASH = 1 << 30;
// all regular hashes have both these bits set
// to be different from either 0, TOMBPRIMEHASH or ZEROHASH
// having only these bits set in a case of Ref key means that the slot is permanently deleted.
protected const int SPECIAL_HASH_BITS = TOMBPRIMEHASH | ZEROHASH;
// Heuristic to decide if we have reprobed toooo many times. Running over
// the reprobe limit on a 'get' call acts as a 'miss'; on a 'put' call it
// can trigger a table resize. Several places must have exact agreement on
// what the reprobe_limit is, so we share it here.
protected const int REPROBE_LIMIT = 4;
protected const int REPROBE_LIMIT_SHIFT = 8;
protected static int ReprobeLimit(int lenMask)
{
// 1/2 of table with some extra
return REPROBE_LIMIT + (lenMask >> REPROBE_LIMIT_SHIFT);
}
protected static bool EntryValueNullOrDead(object entryValue)
{
return entryValue == null || entryValue == TOMBSTONE;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected static int ReduceHashToIndex(int fullHash, int lenMask)
{
var h = (uint)fullHash;
// xor-shift some upper bits down, in case if variations are mostly in high bits
// and scatter the bits a little to break up clusters if hashes are periodic (like 42, 43, 44, ...)
// long clusters can cause long reprobes. small clusters are ok though.
h ^= h >> 15;
h ^= h >> 8;
h += (h >> 3) * 2654435769u;
return (int)h & lenMask;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static object ToObjectValue<TValue>(TValue value)
{
if (default(TValue) != null)
{
return new Boxed<TValue>(value);
}
return (object)value ?? NULLVALUE;
}
}
}

View File

@@ -0,0 +1,138 @@
// Copyright (c) Vladimir Sadov. All rights reserved.
//
// This file is distributed under the MIT License. See LICENSE.md for details.
#nullable disable
using System.Runtime.CompilerServices;
namespace System.Collections.Concurrent
{
internal sealed class DictionaryImplBoxed<TKey, TValue>
: DictionaryImpl<TKey, Boxed<TKey>, TValue>
{
internal DictionaryImplBoxed(int capacity, NonBlockingDictionary<TKey, TValue> topDict)
: base(capacity, topDict)
{
}
internal DictionaryImplBoxed(int capacity, DictionaryImplBoxed<TKey, TValue> other)
: base(capacity, other)
{
}
protected override bool TryClaimSlotForPut(ref Boxed<TKey> entryKey, TKey key)
{
var entryKeyValue = entryKey;
if (entryKeyValue == null)
{
entryKeyValue = Interlocked.CompareExchange(ref entryKey, new Boxed<TKey>(key), null);
if (entryKeyValue == null)
{
// claimed a new slot
this.allocatedSlotCount.Increment();
return true;
}
}
return _keyComparer.Equals(key, entryKeyValue.Value);
}
protected override bool TryClaimSlotForCopy(ref Boxed<TKey> entryKey, Boxed<TKey> key)
{
var entryKeyValue = entryKey;
if (entryKeyValue == null)
{
entryKeyValue = Interlocked.CompareExchange(ref entryKey, key, null);
if (entryKeyValue == null)
{
// claimed a new slot
this.allocatedSlotCount.Increment();
return true;
}
}
return _keyComparer.Equals(key.Value, entryKeyValue.Value);
}
protected override bool keyEqual(TKey key, Boxed<TKey> entryKey)
{
//NOTE: slots are claimed in two stages - claim a hash, then set a key
// it is possible to observe a slot with a null key, but with hash already set
// that is not a match since the key is not yet in the table
if (entryKey == null)
{
return false;
}
return _keyComparer.Equals(key, entryKey.Value);
}
protected override DictionaryImpl<TKey, Boxed<TKey>, TValue> CreateNew(int capacity)
{
return new DictionaryImplBoxed<TKey, TValue>(capacity, this);
}
protected override TKey keyFromEntry(Boxed<TKey> entryKey)
{
return entryKey.Value;
}
}
#pragma warning disable CS0659 // Type overrides Object.Equals(object o) but does not override Object.GetHashCode()
internal class Boxed<T>
{
// 0 - allow writes, 1 - someone is writing, 2 frozen.
public int writeStatus;
public T Value;
public Boxed(T key)
{
this.Value = key;
}
public override bool Equals(object obj)
{
return EqualityComparer<T>.Default.Equals(this.Value, Unsafe.As<Boxed<T>>(obj).Value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryVolatileWrite(T value)
{
if (Interlocked.CompareExchange(ref writeStatus, 1, 0) == 0)
{
Value = value;
Volatile.Write(ref writeStatus, 0);
return true;
}
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryCompareExchange(T oldValue, T newValue, out bool changed)
{
changed = false;
if (Interlocked.CompareExchange(ref writeStatus, 1, 0) != 0)
{
return false;
}
if (EqualityComparer<T>.Default.Equals(Value, oldValue))
{
Value = newValue;
changed = true;
}
Volatile.Write(ref writeStatus, 0);
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Freeze()
{
// Wait for writers (1) to leave. Already 2 is ok, or set 0 -> 2.
while (Interlocked.CompareExchange(ref writeStatus, 2, 0) == 1) ;
}
}
#pragma warning restore CS0659 // Type overrides Object.Equals(object o) but does not override Object.GetHashCode()
}

View File

@@ -0,0 +1,143 @@
// Copyright (c) Vladimir Sadov. All rights reserved.
//
// This file is distributed under the MIT License. See LICENSE.md for details.
namespace System.Collections.Concurrent
{
internal sealed class DictionaryImplInt<TValue>
: DictionaryImpl<int, int, TValue>
{
internal DictionaryImplInt(int capacity, NonBlockingDictionary<int, TValue> topDict)
: base(capacity, topDict)
{
}
internal DictionaryImplInt(int capacity, DictionaryImplInt<TValue> other)
: base(capacity, other)
{
}
protected override bool TryClaimSlotForPut(ref int entryKey, int key)
{
return TryClaimSlot(ref entryKey, key);
}
protected override bool TryClaimSlotForCopy(ref int entryKey, int key)
{
return TryClaimSlot(ref entryKey, key);
}
private bool TryClaimSlot(ref int entryKey, int key)
{
var entryKeyValue = entryKey;
//zero keys are claimed via hash
if (entryKeyValue == 0 & key != 0)
{
entryKeyValue = Interlocked.CompareExchange(ref entryKey, key, 0);
if (entryKeyValue == 0)
{
// claimed a new slot
this.allocatedSlotCount.Increment();
return true;
}
}
return key == entryKeyValue || _keyComparer.Equals(key, entryKeyValue);
}
protected override int hash(int key)
{
if (key == 0)
{
return ZEROHASH;
}
return base.hash(key);
}
protected override bool keyEqual(int key, int entryKey)
{
return key == entryKey || _keyComparer.Equals(key, entryKey);
}
protected override DictionaryImpl<int, int, TValue> CreateNew(int capacity)
{
return new DictionaryImplInt<TValue>(capacity, this);
}
protected override int keyFromEntry(int entryKey)
{
return entryKey;
}
}
internal sealed class DictionaryImplIntNoComparer<TValue>
: DictionaryImpl<int, int, TValue>
{
internal DictionaryImplIntNoComparer(int capacity, NonBlockingDictionary<int, TValue> topDict)
: base(capacity, topDict)
{
}
internal DictionaryImplIntNoComparer(int capacity, DictionaryImplIntNoComparer<TValue> other)
: base(capacity, other)
{
}
protected override bool TryClaimSlotForPut(ref int entryKey, int key)
{
return TryClaimSlot(ref entryKey, key);
}
protected override bool TryClaimSlotForCopy(ref int entryKey, int key)
{
return TryClaimSlot(ref entryKey, key);
}
private bool TryClaimSlot(ref int entryKey, int key)
{
var entryKeyValue = entryKey;
//zero keys are claimed via hash
if (entryKeyValue == 0 & key != 0)
{
entryKeyValue = Interlocked.CompareExchange(ref entryKey, key, 0);
if (entryKeyValue == 0)
{
// claimed a new slot
this.allocatedSlotCount.Increment();
return true;
}
}
return key == entryKeyValue;
}
// inline the base implementation to devirtualize calls to hash and keyEqual
internal override object TryGetValue(int key)
{
return base.TryGetValue(key);
}
protected override int hash(int key)
{
return (key == 0) ?
ZEROHASH :
key | SPECIAL_HASH_BITS;
}
protected override bool keyEqual(int key, int entryKey)
{
return key == entryKey;
}
protected override DictionaryImpl<int, int, TValue> CreateNew(int capacity)
{
return new DictionaryImplIntNoComparer<TValue>(capacity, this);
}
protected override int keyFromEntry(int entryKey)
{
return entryKey;
}
}
}

View File

@@ -0,0 +1,143 @@
// Copyright (c) Vladimir Sadov. All rights reserved.
//
// This file is distributed under the MIT License. See LICENSE.md for details.
namespace System.Collections.Concurrent
{
internal sealed class DictionaryImplLong<TValue>
: DictionaryImpl<long, long, TValue>
{
internal DictionaryImplLong(int capacity, NonBlockingDictionary<long, TValue> topDict)
: base(capacity, topDict)
{
}
internal DictionaryImplLong(int capacity, DictionaryImplLong<TValue> other)
: base(capacity, other)
{
}
protected override bool TryClaimSlotForPut(ref long entryKey, long key)
{
return TryClaimSlot(ref entryKey, key);
}
protected override bool TryClaimSlotForCopy(ref long entryKey, long key)
{
return TryClaimSlot(ref entryKey, key);
}
private bool TryClaimSlot(ref long entryKey, long key)
{
var entryKeyValue = entryKey;
//zero keys are claimed via hash
if (entryKeyValue == 0 && key != 0)
{
entryKeyValue = Interlocked.CompareExchange(ref entryKey, key, 0);
if (entryKeyValue == 0)
{
// claimed a new slot
this.allocatedSlotCount.Increment();
return true;
}
}
return key == entryKeyValue || _keyComparer.Equals(key, entryKeyValue);
}
protected override int hash(long key)
{
if (key == 0)
{
return ZEROHASH;
}
return base.hash(key);
}
protected override bool keyEqual(long key, long entryKey)
{
return key == entryKey || _keyComparer.Equals(key, entryKey);
}
protected override DictionaryImpl<long, long, TValue> CreateNew(int capacity)
{
return new DictionaryImplLong<TValue>(capacity, this);
}
protected override long keyFromEntry(long entryKey)
{
return entryKey;
}
}
internal sealed class DictionaryImplLongNoComparer<TValue>
: DictionaryImpl<long, long, TValue>
{
internal DictionaryImplLongNoComparer(int capacity, NonBlockingDictionary<long, TValue> topDict)
: base(capacity, topDict)
{
}
internal DictionaryImplLongNoComparer(int capacity, DictionaryImplLongNoComparer<TValue> other)
: base(capacity, other)
{
}
protected override bool TryClaimSlotForPut(ref long entryKey, long key)
{
return TryClaimSlot(ref entryKey, key);
}
protected override bool TryClaimSlotForCopy(ref long entryKey, long key)
{
return TryClaimSlot(ref entryKey, key);
}
private bool TryClaimSlot(ref long entryKey, long key)
{
var entryKeyValue = entryKey;
//zero keys are claimed via hash
if (entryKeyValue == 0 && key != 0)
{
entryKeyValue = Interlocked.CompareExchange(ref entryKey, key, 0);
if (entryKeyValue == 0)
{
// claimed a new slot
this.allocatedSlotCount.Increment();
return true;
}
}
return key == entryKeyValue;
}
// inline the base implementation to devirtualize calls to hash and keyEqual
internal override object TryGetValue(long key)
{
return base.TryGetValue(key);
}
protected override int hash(long key)
{
return (key == 0) ?
ZEROHASH :
key.GetHashCode() | SPECIAL_HASH_BITS;
}
protected override bool keyEqual(long key, long entryKey)
{
return key == entryKey;
}
protected override DictionaryImpl<long, long, TValue> CreateNew(int capacity)
{
return new DictionaryImplLongNoComparer<TValue>(capacity, this);
}
protected override long keyFromEntry(long entryKey)
{
return entryKey;
}
}
}

View File

@@ -0,0 +1,143 @@
// Copyright (c) Vladimir Sadov. All rights reserved.
//
// This file is distributed under the MIT License. See LICENSE.md for details.
namespace System.Collections.Concurrent
{
internal sealed class DictionaryImplNint<TValue>
: DictionaryImpl<nint, nint, TValue>
{
internal DictionaryImplNint(int capacity, NonBlockingDictionary<nint, TValue> topDict)
: base(capacity, topDict)
{
}
internal DictionaryImplNint(int capacity, DictionaryImplNint<TValue> other)
: base(capacity, other)
{
}
protected override bool TryClaimSlotForPut(ref nint entryKey, nint key)
{
return TryClaimSlot(ref entryKey, key);
}
protected override bool TryClaimSlotForCopy(ref nint entryKey, nint key)
{
return TryClaimSlot(ref entryKey, key);
}
private bool TryClaimSlot(ref nint entryKey, nint key)
{
var entryKeyValue = entryKey;
//zero keys are claimed via hash
if (entryKeyValue == 0 && key != 0)
{
entryKeyValue = Interlocked.CompareExchange(ref entryKey, key, (nint)0);
if (entryKeyValue == 0)
{
// claimed a new slot
this.allocatedSlotCount.Increment();
return true;
}
}
return key == entryKeyValue || _keyComparer.Equals(key, entryKeyValue);
}
protected override int hash(nint key)
{
if (key == 0)
{
return ZEROHASH;
}
return base.hash(key);
}
protected override bool keyEqual(nint key, nint entryKey)
{
return key == entryKey || _keyComparer.Equals(key, entryKey);
}
protected override DictionaryImpl<nint, nint, TValue> CreateNew(int capacity)
{
return new DictionaryImplNint<TValue>(capacity, this);
}
protected override nint keyFromEntry(nint entryKey)
{
return entryKey;
}
}
internal sealed class DictionaryImplNintNoComparer<TValue>
: DictionaryImpl<nint, nint, TValue>
{
internal DictionaryImplNintNoComparer(int capacity, NonBlockingDictionary<nint, TValue> topDict)
: base(capacity, topDict)
{
}
internal DictionaryImplNintNoComparer(int capacity, DictionaryImplNintNoComparer<TValue> other)
: base(capacity, other)
{
}
protected override bool TryClaimSlotForPut(ref nint entryKey, nint key)
{
return TryClaimSlot(ref entryKey, key);
}
protected override bool TryClaimSlotForCopy(ref nint entryKey, nint key)
{
return TryClaimSlot(ref entryKey, key);
}
private bool TryClaimSlot(ref nint entryKey, nint key)
{
var entryKeyValue = entryKey;
//zero keys are claimed via hash
if (entryKeyValue == 0 && key != 0)
{
entryKeyValue = Interlocked.CompareExchange(ref entryKey, key, (nint)0);
if (entryKeyValue == 0)
{
// claimed a new slot
this.allocatedSlotCount.Increment();
return true;
}
}
return key == entryKeyValue;
}
// inline the base implementation to devirtualize calls to hash and keyEqual
internal override object TryGetValue(nint key)
{
return base.TryGetValue(key);
}
protected override int hash(nint key)
{
return (key == 0) ?
ZEROHASH :
key.GetHashCode() | SPECIAL_HASH_BITS;
}
protected override bool keyEqual(nint key, nint entryKey)
{
return key == entryKey;
}
protected override DictionaryImpl<nint, nint, TValue> CreateNew(int capacity)
{
return new DictionaryImplNintNoComparer<TValue>(capacity, this);
}
protected override nint keyFromEntry(nint entryKey)
{
return entryKey;
}
}
}

View File

@@ -0,0 +1,95 @@
// Copyright (c) Vladimir Sadov. All rights reserved.
//
// This file is distributed under the MIT License. See LICENSE.md for details.
#nullable disable
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace System.Collections.Concurrent
{
internal sealed class DictionaryImplRef<TKey, TKeyStore, TValue>
: DictionaryImpl<TKey, TKey, TValue>
{
internal DictionaryImplRef(int capacity, NonBlockingDictionary<TKey, TValue> topDict)
: base(capacity, topDict)
{
Debug.Assert(!typeof(TKey).IsValueType);
}
internal DictionaryImplRef(int capacity, DictionaryImplRef<TKey, TKeyStore, TValue> other)
: base(capacity, other)
{
Debug.Assert(!typeof(TKey).IsValueType);
}
protected override bool TryClaimSlotForPut(ref TKey entryKey, TKey key)
{
return TryClaimSlot(ref entryKey, key);
}
protected override bool TryClaimSlotForCopy(ref TKey entryKey, TKey key)
{
return TryClaimSlot(ref entryKey, key);
}
private bool TryClaimSlot(ref TKey entryKey, TKey key)
{
ref object keyLocation = ref Unsafe.As<TKey, object>(ref entryKey);
object entryKeyValue = keyLocation;
if (entryKeyValue == null)
{
entryKeyValue = Interlocked.CompareExchange(ref keyLocation, key, null);
if (entryKeyValue == null)
{
// claimed a new slot
this.allocatedSlotCount.Increment();
return true;
}
}
return (object)key == entryKeyValue ||
_keyComparer.Equals(key, Unsafe.As<object, TKey>(ref entryKeyValue));
}
// inline the base implementation to devirtualize calls to hash and keyEqual
internal override object TryGetValue(TKey key)
{
return base.TryGetValue(key);
}
protected override int hash(TKey key)
{
return base.hash(key);
}
protected override bool keyEqual(TKey key, TKey entryKey)
{
if ((object)key == (object)entryKey)
{
return true;
}
//NOTE: slots are claimed in two stages - claim a hash, then set a key
// it is possible to observe a slot with a null key, but with hash already set
// that is not a match since the key is not yet in the table
if (entryKey == null)
{
return false;
}
return _keyComparer.Equals(entryKey, key);
}
protected override DictionaryImpl<TKey, TKey, TValue> CreateNew(int capacity)
{
return new DictionaryImplRef<TKey, TKeyStore, TValue>(capacity, this);
}
protected override TKey keyFromEntry(TKey entryKey)
{
return entryKey;
}
}
}

View File

@@ -0,0 +1,81 @@
// Copyright (c) Vladimir Sadov. All rights reserved.
//
// This file is distributed under the MIT License. See LICENSE.md for details.
#nullable disable
using System.Collections;
using System.Runtime.CompilerServices;
namespace System.Collections.Concurrent
{
internal abstract class DictionaryImpl<TKey, TValue>
: DictionaryImpl
{
private readonly bool _valueIsValueType = typeof(TValue).IsValueType;
internal IEqualityComparer<TKey> _keyComparer;
internal abstract void Clear();
internal abstract int Count { get; }
internal abstract object TryGetValue(TKey key);
internal abstract bool PutIfMatch(TKey key, TValue newVal, ref TValue oldValue, ValueMatch match);
internal abstract bool RemoveIfMatch(TKey key, ref TValue oldValue, ValueMatch match);
internal abstract TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory);
internal abstract Snapshot GetSnapshot();
internal abstract class Snapshot
{
protected int _idx;
protected TKey _curKey;
protected TValue _curValue;
public abstract int Count { get; }
public abstract bool MoveNext();
public abstract void Reset();
internal DictionaryEntry Entry
{
get
{
return new DictionaryEntry(_curKey, _curValue);
}
}
internal KeyValuePair<TKey, TValue> Current
{
get
{
return new KeyValuePair<TKey, TValue>(this._curKey, _curValue);
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected TValue FromObjectValue(object obj)
{
// regular value type
if (default(TValue) != null)
{
return Unsafe.As<Boxed<TValue>>(obj).Value;
}
// null
if (obj == NULLVALUE)
{
return default(TValue);
}
// ref type
if (!_valueIsValueType)
{
return Unsafe.As<object, TValue>(ref obj);
}
// nullable
return (TValue)obj;
}
}
}

View File

@@ -0,0 +1,204 @@
// Copyright (c) Vladimir Sadov. All rights reserved.
//
// This file is distributed under the MIT License. See LICENSE.md for details.
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace System.Collections.Concurrent
{
/// <summary>
/// Scalable 32bit counter that can be used from multiple threads.
/// </summary>
public sealed class Counter32 : CounterBase
{
private class Cell
{
[StructLayout(LayoutKind.Explicit, Size = CACHE_LINE * 2 - OBJ_HEADER_SIZE)]
public struct SpacedCounter
{
[FieldOffset(CACHE_LINE - OBJ_HEADER_SIZE)]
public int count;
}
public SpacedCounter counter;
}
// spaced out counters
private Cell[]? cells;
// default counter
private int count;
// delayed estimated count
private int lastCount;
/// <summary>
/// Returns the value of the counter at the time of the call.
/// </summary>
/// <remarks>
/// The value may miss in-progress updates if the counter is being concurrently modified.
/// </remarks>
public int Value
{
get
{
var count = this.count;
var cells = this.cells;
if (cells != null)
{
for (int i = 0; i < cells.Length; i++)
{
var cell = cells[i];
if (cell != null)
{
count += cell.counter.count;
}
else
{
break;
}
}
}
return count;
}
}
/// <summary>
/// Returns the approximate value of the counter at the time of the call.
/// </summary>
/// <remarks>
/// EstimatedValue could be significantly cheaper to obtain, but may be slightly delayed.
/// </remarks>
public int EstimatedValue
{
get
{
if (this.cells == null)
{
return this.count;
}
var curTicks = (uint)Environment.TickCount;
// more than a millisecond passed?
if (curTicks != lastCountTicks)
{
lastCountTicks = curTicks;
lastCount = Value;
}
return lastCount;
}
}
/// <summary>
/// Increments the counter by 1.
/// </summary>
public void Increment()
{
int curCellCount = this.cellCount;
var drift = increment(ref GetCountRef(curCellCount));
if (drift != 0)
{
TryAddCell(curCellCount);
}
}
/// <summary>
/// Decrements the counter by 1.
/// </summary>
public void Decrement()
{
int curCellCount = this.cellCount;
var drift = decrement(ref GetCountRef(curCellCount));
if (drift != 0)
{
TryAddCell(curCellCount);
}
}
/// <summary>
/// Increments the counter by 'value'.
/// </summary>
public void Add(int value)
{
int curCellCount = this.cellCount;
var drift = add(ref GetCountRef(curCellCount), value);
if (drift != 0)
{
TryAddCell(curCellCount);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ref int GetCountRef(int curCellCount)
{
ref var countRef = ref count;
Cell[]? cells;
if ((cells = this.cells) != null && curCellCount > 1)
{
var cell = cells[GetIndex((uint)curCellCount)];
if (cell != null)
{
countRef = ref cell.counter.count;
}
}
return ref countRef;
}
private static int increment(ref int val)
{
return -val - 1 + Interlocked.Increment(ref val);
}
private static int add(ref int val, int inc)
{
return -val - inc + Interlocked.Add(ref val, inc);
}
private static int decrement(ref int val)
{
return val - 1 - Interlocked.Decrement(ref val);
}
private void TryAddCell(int curCellCount)
{
if (curCellCount < s_MaxCellCount)
{
TryAddCellCore(curCellCount);
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void TryAddCellCore(int curCellCount)
{
var cells = this.cells;
if (cells == null)
{
var newCells = new Cell[s_MaxCellCount];
cells = Interlocked.CompareExchange(ref this.cells, newCells, null) ?? newCells;
}
if (cells[curCellCount] == null)
{
Interlocked.CompareExchange(ref cells[curCellCount], new Cell(), null);
}
if (this.cellCount == curCellCount)
{
Interlocked.CompareExchange(ref this.cellCount, curCellCount + 1, curCellCount);
//if (Interlocked.CompareExchange(ref this.cellCount, curCellCount + 1, curCellCount) == curCellCount)
//{
// System.Console.WriteLine(curCellCount + 1);
//}
}
}
}
}

View File

@@ -0,0 +1,203 @@
// Copyright (c) Vladimir Sadov. All rights reserved.
//
// This file is distributed under the MIT License. See LICENSE.md for details.
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace System.Collections.Concurrent
{
/// <summary>
/// Scalable 64bit counter that can be used from multiple threads.
/// </summary>
public sealed class Counter64 : CounterBase
{
private class Cell
{
[StructLayout(LayoutKind.Explicit, Size = CACHE_LINE * 2 - OBJ_HEADER_SIZE)]
public struct SpacedCounter
{
[FieldOffset(CACHE_LINE - OBJ_HEADER_SIZE)]
public long count;
}
public SpacedCounter counter;
}
// spaced out counters
private Cell[]? cells;
// default counter
private long count;
// delayed count
private long lastCount;
/// <summary>
/// Returns the value of the counter at the time of the call.
/// </summary>
/// <remarks>
/// The value may miss in-progress updates if the counter is being concurrently modified.
/// </remarks>
public long Value
{
get
{
var count = this.count;
var cells = this.cells;
if (cells != null)
{
for (int i = 0; i < cells.Length; i++)
{
var cell = cells[i];
if (cell != null)
{
count += cell.counter.count;
}
else
{
break;
}
}
}
return count;
}
}
/// <summary>
/// Returns the approximate value of the counter at the time of the call.
/// </summary>
/// <remarks>
/// EstimatedValue could be significantly cheaper to obtain, but may be slightly delayed.
/// </remarks>
public long EstimatedValue
{
get
{
if (this.cellCount == 0)
{
return Value;
}
var curTicks = (uint)Environment.TickCount;
// more than a millisecond passed?
if (curTicks != lastCountTicks)
{
lastCountTicks = curTicks;
lastCount = Value;
}
return lastCount;
}
}
/// <summary>
/// Increments the counter by 1.
/// </summary>
public void Increment()
{
int curCellCount = this.cellCount;
var drift = increment(ref GetCountRef(curCellCount));
if (drift != 0)
{
TryAddCell(curCellCount);
}
}
/// <summary>
/// Decrements the counter by 1.
/// </summary>
public void Decrement()
{
int curCellCount = this.cellCount;
var drift = decrement(ref GetCountRef(curCellCount));
if (drift != 0)
{
TryAddCell(curCellCount);
}
}
/// <summary>
/// Increments the counter by 'value'.
/// </summary>
public void Add(int value)
{
int curCellCount = this.cellCount;
var drift = add(ref GetCountRef(curCellCount), value);
if (drift != 0)
{
TryAddCell(curCellCount);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ref long GetCountRef(int curCellCount)
{
ref var countRef = ref count;
Cell[]? cells;
if ((cells = this.cells) != null && curCellCount > 1)
{
var cell = cells[GetIndex((uint)curCellCount)];
if (cell != null)
{
countRef = ref cell.counter.count;
}
}
return ref countRef;
}
private static long increment(ref long val)
{
return -val - 1 + Interlocked.Increment(ref val);
}
private static long add(ref long val, int inc)
{
return -val - inc + Interlocked.Add(ref val, inc);
}
private static long decrement(ref long val)
{
return val - 1 - Interlocked.Decrement(ref val);
}
private void TryAddCell(int curCellCount)
{
if (curCellCount < s_MaxCellCount)
{
TryAddCellCore(curCellCount);
}
}
private void TryAddCellCore(int curCellCount)
{
var cells = this.cells;
if (cells == null)
{
var newCells = new Cell[s_MaxCellCount];
cells = Interlocked.CompareExchange(ref this.cells, newCells, null) ?? newCells;
}
if (cells[curCellCount] == null)
{
Interlocked.CompareExchange(ref cells[curCellCount], new Cell(), null);
}
if (this.cellCount == curCellCount)
{
Interlocked.CompareExchange(ref this.cellCount, curCellCount + 1, curCellCount);
//if (Interlocked.CompareExchange(ref this.cellCount, curCellCount + 1, curCellCount) == curCellCount)
//{
// System.Console.WriteLine(curCellCount + 1);
//}
}
}
}
}

View File

@@ -0,0 +1,38 @@
// Copyright (c) Vladimir Sadov. All rights reserved.
//
// This file is distributed under the MIT License. See LICENSE.md for details.
using System.Runtime.CompilerServices;
namespace System.Collections.Concurrent
{
/// <summary>
/// Scalable counter base.
/// </summary>
public class CounterBase
{
private protected const int CACHE_LINE = 64;
private protected const int OBJ_HEADER_SIZE = 8;
private protected static readonly int s_MaxCellCount = Util.AlignToPowerOfTwo(Environment.ProcessorCount) + 1;
// how many cells we have
private protected int cellCount;
// delayed count time
private protected uint lastCountTicks;
private protected CounterBase()
{
// touch static
_ = s_MaxCellCount;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private protected unsafe static int GetIndex(uint cellCount)
{
nuint addr = (nuint)(&cellCount);
return (int)(addr % cellCount);
}
}
}

View File

@@ -0,0 +1,21 @@
using System.Diagnostics;
namespace System.Collections.Concurrent
{
internal static class Util
{
// returns 2^x >= size
internal static int AlignToPowerOfTwo(int size)
{
Debug.Assert(size > 0);
size--;
size |= size >> 1;
size |= size >> 2;
size |= size >> 4;
size |= size >> 8;
size |= size >> 16;
return size + 1;
}
}
}

View File

@@ -44,7 +44,7 @@ public class ObjectPool<T> : DisposeBase, IPool<T> where T : notnull
private readonly ConcurrentQueue<Item> _free2 = new();
/// <summary>借出去的放在这</summary>
private readonly ConcurrentDictionary<T, Item> _busy = new();
private readonly NonBlockingDictionary<T, Item> _busy = new();
//private readonly Object SyncRoot = new();
#endregion

View File

@@ -1,5 +1,4 @@
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
using ThingsGateway.NewLife.Threading;
namespace ThingsGateway.NewLife;
@@ -52,7 +51,7 @@ public class ExpiringDictionary<TKey, TValue> : IDisposable
return rs;
}
}
private ConcurrentDictionary<TKey, CacheItem> _dict;
private NonBlockingDictionary<TKey, CacheItem> _dict;
private readonly TimerX _cleanupTimer;
private int defaultExpire = 60;
@@ -61,7 +60,7 @@ public class ExpiringDictionary<TKey, TValue> : IDisposable
{
defaultExpire = expire;
this.comparer = comparer;
_dict = new ConcurrentDictionary<TKey, CacheItem>(comparer);
_dict = new NonBlockingDictionary<TKey, CacheItem>(comparer);
_cleanupTimer = new TimerX(TimerClear, null, 10000, 10000) { Async = true };
}

View File

@@ -13,8 +13,8 @@ public class FastMapperOption
public static class FastMapper
{
// 泛型 + 非泛型共用缓存
private static readonly ConcurrentDictionary<(Type Source, Type Target), Delegate> _mapCache
= new ConcurrentDictionary<(Type, Type), Delegate>();
private static readonly NonBlockingDictionary<(Type Source, Type Target), Delegate> _mapCache
= new NonBlockingDictionary<(Type, Type), Delegate>();
#region
public static TTarget Mapper<TSource, TTarget>(TSource source, FastMapperOption option = null)

View File

@@ -168,8 +168,8 @@ public class CompositeConfigProvider : IConfigProvider
#endregion
#region
private readonly ConcurrentDictionary<Object, String> _models = [];
private readonly ConcurrentDictionary<Object, ModelWrap> _models2 = [];
private readonly NonBlockingDictionary<Object, String> _models = [];
private readonly NonBlockingDictionary<Object, ModelWrap> _models2 = [];
/// <summary>绑定模型,使能热更新,配置存储数据改变时同步修改模型属性</summary>
/// <typeparam name="T">模型。可通过实现IConfigMapping接口来自定义映射配置到模型实例</typeparam>
/// <param name="model">模型实例</param>

View File

@@ -11,7 +11,7 @@ public static class DictionaryExtensions
/// <param name="dict"></param>
/// <param name="key"></param>
/// <returns></returns>
public static Boolean Remove<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> dict, TKey key) where TKey : notnull => dict.TryRemove(key, out _);
public static Boolean Remove<TKey, TValue>(this NonBlockingDictionary<TKey, TValue> dict, TKey key) where TKey : notnull => dict.TryRemove(key, out _);
#if !NET6_0_OR_GREATER
public static bool TryAdd<TKey, TValue>(this IDictionary<TKey, TValue> pairs, TKey key, TValue value)
@@ -77,7 +77,7 @@ public static class DictionaryExtensions
/// <summary>
/// 批量出队
/// </summary>
public static List<T> ToListWithDequeue<TKEY, T>(this ConcurrentDictionary<TKEY, T> values, int maxCount = 0)
public static List<T> ToListWithDequeue<TKEY, T>(this NonBlockingDictionary<TKEY, T> values, int maxCount = 0)
{
if (maxCount <= 0)
{
@@ -105,7 +105,7 @@ public static class DictionaryExtensions
/// <summary>
/// 批量出队
/// </summary>
public static Dictionary<TKEY, T> ToDictWithDequeue<TKEY, T>(this ConcurrentDictionary<TKEY, T> values, int maxCount = 0)
public static Dictionary<TKEY, T> ToDictWithDequeue<TKEY, T>(this NonBlockingDictionary<TKEY, T> values, int maxCount = 0)
{
if (maxCount <= 0)
{
@@ -135,7 +135,7 @@ public static class DictionaryExtensions
/// <summary>
/// 批量出队
/// </summary>
public static IEnumerable<KeyValuePair<TKEY, T>> ToIEnumerableKVWithDequeue<TKEY, T>(this ConcurrentDictionary<TKEY, T> values, int maxCount = 0)
public static IEnumerable<KeyValuePair<TKEY, T>> ToIEnumerableKVWithDequeue<TKEY, T>(this NonBlockingDictionary<TKEY, T> values, int maxCount = 0)
{
if (values.IsEmpty) yield break;

View File

@@ -101,7 +101,7 @@ public static class LinqExtensions
/// <summary>
/// 从并发字典中删除
/// </summary>
public static bool Remove<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> dict, TKey key) where TKey : notnull
public static bool Remove<TKey, TValue>(this NonBlockingDictionary<TKey, TValue> dict, TKey key) where TKey : notnull
{
return dict.TryRemove(key, out TValue? _);
}

View File

@@ -97,7 +97,7 @@ public class ConsoleLog : Logger
}
}
static readonly ConcurrentDictionary<Int32, ConsoleColor> dic = new();
static readonly NonBlockingDictionary<Int32, ConsoleColor> dic = new();
static readonly ConsoleColor[] colors = [
ConsoleColor.Green, ConsoleColor.Cyan, ConsoleColor.Magenta, ConsoleColor.White, ConsoleColor.Yellow,
ConsoleColor.DarkGreen, ConsoleColor.DarkCyan, ConsoleColor.DarkMagenta, ConsoleColor.DarkRed, ConsoleColor.DarkYellow ];

View File

@@ -109,7 +109,7 @@ public class DefaultTracer : DisposeBase, ITracer, ILogFeature
public IPool<ISpan> SpanPool => _SpanPool ??= new MySpanPool();
/// <summary>Span构建器集合</summary>
protected ConcurrentDictionary<String, ISpanBuilder> _builders = new();
protected NonBlockingDictionary<String, ISpanBuilder> _builders = new();
/// <summary>采样定时器</summary>
protected TimerX? _timer;
@@ -292,7 +292,7 @@ public class DefaultTracer : DisposeBase, ITracer, ILogFeature
var bs = _builders;
if (bs.IsEmpty) return [];
_builders = new ConcurrentDictionary<String, ISpanBuilder>();
_builders = new NonBlockingDictionary<String, ISpanBuilder>();
var bs2 = bs.Values.Where(e => e.Total > 0).ToArray();

View File

@@ -64,7 +64,7 @@ public class TextFileLog : Logger, IDisposable
_Timer = new TimerX(DoWriteAndClose, null, 0_000, 5_000) { Async = true };
}
private static readonly ConcurrentDictionary<String, TextFileLog> cache = new(StringComparer.OrdinalIgnoreCase);
private static readonly NonBlockingDictionary<String, TextFileLog> cache = new(StringComparer.OrdinalIgnoreCase);
/// <summary>每个目录的日志实例应该只有一个,所以采用静态创建</summary>
/// <param name="path">日志目录或日志文件路径</param>
/// <param name="fileFormat"></param>
@@ -234,7 +234,7 @@ public class TextFileLog : Logger, IDisposable
if (!_isFile && Backups > 0)
{
// 判断日志目录是否已存在
var di = LogPath.GetBasePath().AsDirectory();
DirectoryInfo? di = new DirectoryInfo(LogPath);
if (di.Exists)
{
// 删除*.del
@@ -257,7 +257,7 @@ public class TextFileLog : Logger, IDisposable
{
// 删除最旧的文件
var retain = fis.Length - Backups;
fis = fis.OrderBy(e => e.CreationTime).Take(retain).ToArray();
fis = fis.OrderBy(e => e.LastWriteTimeUtc).Take(retain).ToArray();
foreach (var item in fis)
{
OnWrite(LogLevel.Info, "The log file has reached the maximum limit of {0}, delete {1}, size {2: n0} Byte", Backups, item.Name, item.Length);

View File

@@ -59,7 +59,7 @@ public interface IEventHandler<TEvent>
/// </remarks>
public class EventBus<TEvent> : DisposeBase, IEventBus<TEvent>
{
private readonly ConcurrentDictionary<String, IEventHandler<TEvent>> _handlers = [];
private readonly NonBlockingDictionary<String, IEventHandler<TEvent>> _handlers = [];
/// <summary>已订阅的事件处理器集合</summary>
public IDictionary<String, IEventHandler<TEvent>> Handlers => _handlers;

View File

@@ -20,9 +20,9 @@ public class DeferredQueue : DisposeBase
/// <summary>名称</summary>
public String Name { get; set; }
private volatile ConcurrentDictionary<String, Object> _Entities = new();
private volatile NonBlockingDictionary<String, Object> _Entities = new();
/// <summary>实体字典</summary>
public ConcurrentDictionary<String, Object> Entities => _Entities;
public NonBlockingDictionary<String, Object> Entities => _Entities;
/// <summary>跟踪数。达到该值时输出跟踪日志默认1000</summary>
public Int32 TraceCount { get; set; } = 1000;
@@ -206,7 +206,7 @@ public class DeferredQueue : DisposeBase
var es = _Entities;
if (es.IsEmpty) return;
_Entities = new ConcurrentDictionary<String, Object>();
_Entities = new NonBlockingDictionary<String, Object>();
var times = _Times;
Interlocked.Add(ref _count, -es.Count);

View File

@@ -18,7 +18,7 @@ class MyServiceScope : IServiceScope, IServiceProvider
public IServiceProvider ServiceProvider => this;
private readonly ConcurrentDictionary<Type, Object?> _cache = new();
private readonly NonBlockingDictionary<Type, Object?> _cache = new();
public void Dispose()
{

View File

@@ -23,7 +23,7 @@ public class DnsResolver : IDnsResolver
/// <summary>缓存超时时间</summary>
public TimeSpan Expire { set; get; } = TimeSpan.FromMinutes(5);
private readonly ConcurrentDictionary<String, DnsItem> _cache = new();
private readonly NonBlockingDictionary<String, DnsItem> _cache = new();
/// <summary>解析域名</summary>
/// <param name="host"></param>

View File

@@ -146,7 +146,7 @@ public class NetServer : DisposeBase, IServer, IExtend, ILogFeature
/// </remarks>
public IServiceProvider? ServiceProvider { get; set; }
private ConcurrentDictionary<String, Object?>? _items;
private NonBlockingDictionary<String, Object?>? _items;
/// <summary>数据项</summary>
public IDictionary<String, Object?> Items => _items ??= new();
@@ -486,7 +486,7 @@ public class NetServer : DisposeBase, IServer, IExtend, ILogFeature
#endregion
#region
private readonly ConcurrentDictionary<Int32, INetSession> _Sessions = new();
private readonly NonBlockingDictionary<Int32, INetSession> _Sessions = new();
/// <summary>会话集合。用自增的数字ID作为标识业务应用自己维持ID与业务主键的对应关系。</summary>
public IDictionary<Int32, INetSession> Sessions => _Sessions;

View File

@@ -853,7 +853,7 @@ public abstract class SessionBase : DisposeBase, ISocketClient, ITransport, ILog
#endregion
#region
private ConcurrentDictionary<String, Object?>? _items;
private NonBlockingDictionary<String, Object?>? _items;
/// <summary>数据项</summary>
public IDictionary<String, Object?> Items => _items ??= new();

View File

@@ -10,7 +10,7 @@ namespace ThingsGateway.NewLife.Net;
internal class SessionCollection : DisposeBase, IDictionary<String, ISocketSession>
{
#region
private readonly ConcurrentDictionary<String, ISocketSession> _dic = new();
private readonly NonBlockingDictionary<String, ISocketSession> _dic = new();
/// <summary>服务端</summary>
public ISocketServer Server { get; private set; }

View File

@@ -400,7 +400,7 @@ public class UdpSession : DisposeBase, ISocketSession, ITransport, ILogFeature
#endregion
#region
private ConcurrentDictionary<String, Object?>? _items;
private NonBlockingDictionary<String, Object?>? _items;
/// <summary>数据项</summary>
public IDictionary<String, Object?> Items => _items ??= new();

View File

@@ -212,7 +212,7 @@ public class FullRedis : Redis
}
}
private ConcurrentDictionary<String, IPool<RedisClient>> _pools = new();
private NonBlockingDictionary<String, IPool<RedisClient>> _pools = new();
/// <summary>获取指定节点的连接池</summary>
/// <param name="node"></param>
/// <returns></returns>

View File

@@ -1106,10 +1106,10 @@ public class RedisClient : DisposeBase
#endregion
#region
private static readonly ConcurrentDictionary<String, String> _cache0 = new();
private static readonly ConcurrentDictionary<String, String> _cache1 = new();
private static readonly ConcurrentDictionary<String, String> _cache2 = new();
private static readonly ConcurrentDictionary<String, String> _cache3 = new();
private static readonly NonBlockingDictionary<String, String> _cache0 = new();
private static readonly NonBlockingDictionary<String, String> _cache1 = new();
private static readonly NonBlockingDictionary<String, String> _cache2 = new();
private static readonly NonBlockingDictionary<String, String> _cache3 = new();
/// <summary>获取命令对应的字节数组,全局缓存</summary>
/// <param name="cmd"></param>
/// <param name="args"></param>

View File

@@ -94,7 +94,7 @@ public class AssemblyX
#region
private AssemblyX(Assembly asm) => Asm = asm;
private static readonly ConcurrentDictionary<Assembly, AssemblyX> cache = new();
private static readonly NonBlockingDictionary<Assembly, AssemblyX> cache = new();
/// <summary>创建程序集辅助对象</summary>
/// <param name="asm"></param>
/// <returns></returns>
@@ -234,7 +234,7 @@ public class AssemblyX
#endregion
#region
private readonly ConcurrentDictionary<String, Type?> typeCache2 = new();
private readonly NonBlockingDictionary<String, Type?> typeCache2 = new();
/// <summary>从程序集中查找指定名称的类型</summary>
/// <param name="typeName"></param>
/// <returns></returns>
@@ -268,7 +268,7 @@ public class AssemblyX
#endregion
#region
private readonly ConcurrentDictionary<Type, List<Type>> _plugins = new();
private readonly NonBlockingDictionary<Type, List<Type>> _plugins = new();
/// <summary>查找插件,带缓存</summary>
/// <param name="baseType">类型</param>
/// <returns></returns>

View File

@@ -10,7 +10,7 @@ namespace ThingsGateway.NewLife;
public static class AttributeX
{
#region
private static readonly ConcurrentDictionary<String, Object> _asmCache = new();
private static readonly NonBlockingDictionary<String, Object> _asmCache = new();
/// <summary>获取自定义属性,带有缓存功能,避免因.Net内部GetCustomAttributes没有缓存而带来的损耗</summary>
/// <typeparam name="TAttribute"></typeparam>

View File

@@ -317,8 +317,8 @@ public class DefaultReflect : IReflect
#endregion
#region /
private readonly ConcurrentDictionary<Type, IList<FieldInfo>> _cache1 = new();
private readonly ConcurrentDictionary<Type, IList<FieldInfo>> _cache2 = new();
private readonly NonBlockingDictionary<Type, IList<FieldInfo>> _cache1 = new();
private readonly NonBlockingDictionary<Type, IList<FieldInfo>> _cache2 = new();
/// <summary>获取字段</summary>
/// <param name="type"></param>
/// <param name="baseFirst"></param>
@@ -353,8 +353,8 @@ public class DefaultReflect : IReflect
return list;
}
private readonly ConcurrentDictionary<Type, IList<PropertyInfo>> _cache3 = new();
private readonly ConcurrentDictionary<Type, IList<PropertyInfo>> _cache4 = new();
private readonly NonBlockingDictionary<Type, IList<PropertyInfo>> _cache3 = new();
private readonly NonBlockingDictionary<Type, IList<PropertyInfo>> _cache4 = new();
/// <summary>获取属性</summary>
/// <param name="type"></param>
/// <param name="baseFirst"></param>
@@ -805,7 +805,7 @@ public class DefaultReflect : IReflect
#endregion
#region
//private readonly ConcurrentDictionary<Type, ConcurrentDictionary<Type, Boolean>> _as_cache = new ConcurrentDictionary<Type, ConcurrentDictionary<Type, Boolean>>();
//private readonly NonBlockingDictionary<Type, NonBlockingDictionary<Type, Boolean>> _as_cache = new NonBlockingDictionary<Type, NonBlockingDictionary<Type, Boolean>>();
/// <summary>是否子类</summary>
/// <param name="type"></param>
/// <param name="baseType"></param>
@@ -832,7 +832,7 @@ public class DefaultReflect : IReflect
//var key = $"{type.FullName}_{baseType.FullName}";
//if (!_as_cache.TryGetValue(type, out var dic))
//{
// dic = new ConcurrentDictionary<Type, Boolean>();
// dic = new NonBlockingDictionary<Type, Boolean>();
// _as_cache.TryAdd(type, dic);
//}

View File

@@ -111,7 +111,7 @@ public class ScriptEngine
IsExpression = isExpression;
}
static readonly ConcurrentDictionary<String, ScriptEngine> _cache = new(StringComparer.OrdinalIgnoreCase);
static readonly NonBlockingDictionary<String, ScriptEngine> _cache = new(StringComparer.OrdinalIgnoreCase);
/// <summary>为指定代码片段创建脚本引擎实例。采用缓存,避免同一脚本重复创建引擎。</summary>
/// <param name="code">代码片段</param>
/// <param name="isExpression">是否表达式表达式将编译成为一个Main方法</param>

View File

@@ -243,7 +243,7 @@ public class BinaryComposite : BinaryHandlerBase
throw new NotSupportedException();
}
private static readonly ConcurrentDictionary<MemberInfo, IMemberAccessor?> _cache = new();
private static readonly NonBlockingDictionary<MemberInfo, IMemberAccessor?> _cache = new();
private static Boolean TryGetAccessor(MemberInfo member, [NotNullWhen(true)] out IMemberAccessor? acc)
{
if (_cache.TryGetValue(member, out acc)) return acc != null;

View File

@@ -323,7 +323,7 @@ public class SystemJson : IJsonHost
#region IJsonHost
private static readonly ConcurrentDictionary<string, JsonSerializerOptions> _optionsCache = new();
private static readonly NonBlockingDictionary<string, JsonSerializerOptions> _optionsCache = new();
private static JsonSerializerOptions GetOptions(JsonSerializerOptions jsonSerializerOptions, bool indented, bool nullValue, bool camelCase)
{

View File

@@ -11,7 +11,7 @@ namespace ThingsGateway.NewLife.Serialization;
/// <summary>序列化助手</summary>
public static class SerialHelper
{
private static readonly ConcurrentDictionary<PropertyInfo, String> _cache = new();
private static readonly NonBlockingDictionary<PropertyInfo, String> _cache = new();
/// <summary>获取序列化名称</summary>
/// <param name="pi"></param>
/// <returns></returns>

View File

@@ -318,7 +318,9 @@ public class TimerScheduler : IDisposable, ILogFeature
timer.hasSetNext = false;
using var span = timer.Tracer?.NewSpan(timer.TracerName ?? $"timer:Execute", timer.Timers + "");
string tracerName = timer.TracerName ?? "timer:ExecuteAsync";
string timerArg = timer.Timers.ToString();
using var span = timer.Tracer?.NewSpan(tracerName, timerArg);
var sw = _stopwatchPool.Get();
sw.Restart();
try
@@ -331,9 +333,12 @@ public class TimerScheduler : IDisposable, ILogFeature
timer.Dispose();
return;
}
var func = timer.Method.As<TimerCallback>(target);
func!(timer.State);
if (timer.TimerCallbackCachedDelegate == null)
{
timer.TimerCallbackCachedDelegate = timer.Method.As<TimerCallback>(target);
}
//var func = timer.Method.As<TimerCallback>(target);
timer.TimerCallbackCachedDelegate!(timer.State);
}
catch (ThreadAbortException) { throw; }
catch (ThreadInterruptedException) { throw; }
@@ -367,7 +372,9 @@ public class TimerScheduler : IDisposable, ILogFeature
timer.hasSetNext = false;
using var span = timer.Tracer?.NewSpan(timer.TracerName ?? $"timer:ExecuteAsync", timer.Timers + "");
string tracerName = timer.TracerName ?? "timer:ExecuteAsync";
string timerArg = timer.Timers.ToString();
using var span = timer.Tracer?.NewSpan(tracerName, timerArg);
var sw = _stopwatchPool.Get();
sw.Restart();
try
@@ -381,19 +388,31 @@ public class TimerScheduler : IDisposable, ILogFeature
return;
}
#if NET6_0_OR_GREATER
if (timer.IsValueTask)
{
var func = timer.Method.As<Func<Object?, ValueTask>>(target);
var task = func!(timer.State);
if (timer.ValueTaskCachedDelegate == null)
{
timer.ValueTaskCachedDelegate = timer.Method.As<Func<Object?, ValueTask>>(target);
}
//var func = timer.Method.As<Func<Object?, ValueTask>>(target);
var task = timer.ValueTaskCachedDelegate!(timer.State);
if (!task.IsCompleted)
await task.ConfigureAwait(false);
}
else
#endif
{
var func = timer.Method.As<Func<Object?, Task>>(target);
var task = func!(timer.State);
if (timer.TaskCachedDelegate == null)
{
timer.TaskCachedDelegate = timer.Method.As<Func<Object?, Task>>(target);
}
//var func = timer.Method.As<Func<Object?, Task>>(target);
var task = timer.TaskCachedDelegate!(timer.State);
if (!task.IsCompleted)
await task.ConfigureAwait(false);
}

View File

@@ -542,6 +542,12 @@ public class TimerX : ITimer, ITimerx, IDisposable
}
}
public Func<object?, Task>? TaskCachedDelegate { get; internal set; }
#if NET6_0_OR_GREATER
public Func<object?, ValueTask>? ValueTaskCachedDelegate { get; internal set; }
#endif
public TimerCallback? TimerCallbackCachedDelegate { get; internal set; }
private static void CopyNow(Object? state) => _Now = TimerScheduler.Default.GetNow();
#endregion

View File

@@ -31,7 +31,7 @@ namespace ThingsGateway.SqlSugar
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals,
};
private static readonly ConcurrentDictionary<Type, SugarColumn> _typeInfoCache = new();
private static readonly NonBlockingDictionary<Type, SugarColumn> _typeInfoCache = new();
static SerializeService()
{
@@ -57,6 +57,7 @@ namespace ThingsGateway.SqlSugar
}
});
_systemTextJsonSettings.NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals;
_systemTextJsonSettings.Converters.Add(new JTokenSystemTextJsonConverter());
_systemTextJsonSettings.Converters.Add(new JValueSystemTextJsonConverter());
_systemTextJsonSettings.Converters.Add(new JObjectSystemTextJsonConverter());

View File

@@ -4,7 +4,7 @@ namespace ThingsGateway.SqlSugar
{
public static class CallContextAsync<T>
{
static ConcurrentDictionary<string, AsyncLocal<T>> state = new ConcurrentDictionary<string, AsyncLocal<T>>();
static NonBlockingDictionary<string, AsyncLocal<T>> state = new NonBlockingDictionary<string, AsyncLocal<T>>();
public static void SetData(string name, T data) =>
state.GetOrAdd(name, _ => new AsyncLocal<T>()).Value = data;
public static T GetData(string name) =>
@@ -13,7 +13,7 @@ namespace ThingsGateway.SqlSugar
public static class CallContextThread<T>
{
static ConcurrentDictionary<string, ThreadLocal<T>> state = new ConcurrentDictionary<string, ThreadLocal<T>>();
static NonBlockingDictionary<string, ThreadLocal<T>> state = new NonBlockingDictionary<string, ThreadLocal<T>>();
public static void SetData(string name, T data) =>
state.GetOrAdd(name, _ => new ThreadLocal<T>()).Value = data;
public static T GetData(string name) =>

View File

@@ -8,7 +8,7 @@ namespace ThingsGateway.SqlSugar
{
internal static class FastCopy
{
static ConcurrentDictionary<string, object> copiers = new ConcurrentDictionary<string, object>();
static NonBlockingDictionary<string, object> copiers = new NonBlockingDictionary<string, object>();
/// <summary>
/// 复制两个对象同名属性值

View File

@@ -22,8 +22,8 @@ namespace ThingsGateway.SqlSugar
}
public static class PropertyCallAdapterProvider<TThis>
{
private static readonly System.Collections.Concurrent.ConcurrentDictionary<string, IPropertyCallAdapter<TThis>> _instances =
new System.Collections.Concurrent.ConcurrentDictionary<string, IPropertyCallAdapter<TThis>>();
private static readonly System.Collections.Concurrent.NonBlockingDictionary<string, IPropertyCallAdapter<TThis>> _instances =
new System.Collections.Concurrent.NonBlockingDictionary<string, IPropertyCallAdapter<TThis>>();
public static IPropertyCallAdapter<TThis> GetInstance(string forPropertyName)
{

View File

@@ -1,9 +1,9 @@
<Project>
<PropertyGroup>
<PluginVersion>10.11.104</PluginVersion>
<ProPluginVersion>10.11.104</ProPluginVersion>
<DefaultVersion>10.11.104</DefaultVersion>
<PluginVersion>10.11.110</PluginVersion>
<ProPluginVersion>10.11.110</ProPluginVersion>
<DefaultVersion>10.11.110</DefaultVersion>
<AuthenticationVersion>10.11.6</AuthenticationVersion>
<SourceGeneratorVersion>10.11.6</SourceGeneratorVersion>
<NET8Version>8.0.21</NET8Version>
@@ -12,7 +12,7 @@
<IsTrimmable>false</IsTrimmable>
<ManagementProPluginVersion>10.11.87</ManagementProPluginVersion>
<ManagementPluginVersion>10.11.87</ManagementPluginVersion>
<TSVersion>4.0.0-beta.120</TSVersion>
<TSVersion>4.0.0-beta.130</TSVersion>
</PropertyGroup>

View File

@@ -64,7 +64,7 @@ public class DDPUdpSessionChannel : UdpSessionChannel, IClientChannel, IDtuUdpSe
public EndPoint DefaultEndpoint => RemoteIPHost?.EndPoint;
ConcurrentDictionary<string, WaitLock> WaitLocks { get; } = new();
NonBlockingDictionary<string, WaitLock> WaitLocks { get; } = new();
public override WaitLock GetLock(string key)
{

View File

@@ -19,7 +19,7 @@ namespace ThingsGateway.Foundation;
[DebuggerDisplay("Count={Count}")]
internal class InternalConcurrentDictionary<TKey, TValue> : IEnumerable<TValue>
{
private readonly ConcurrentDictionary<TKey, TValue> m_clients = new();
private readonly NonBlockingDictionary<TKey, TValue> m_clients = new();
public int Count => m_clients.Count;

View File

@@ -26,24 +26,65 @@ public static class ChannelOptionsExtensions
/// <param name="e">接收数据</param>
/// <param name="funcs">事件</param>
/// <returns></returns>
internal static async Task OnChannelReceivedEvent(this IClientChannel clientChannel, ReceivedDataEventArgs e, ChannelReceivedEventHandler funcs)
internal static ValueTask OnChannelReceivedEvent(
this IClientChannel clientChannel,
ReceivedDataEventArgs e,
ChannelReceivedEventHandler funcs)
{
clientChannel.ThrowIfNull(nameof(IClientChannel));
e.ThrowIfNull(nameof(ReceivedDataEventArgs));
funcs.ThrowIfNull(nameof(ChannelReceivedEventHandler));
if (funcs.Count > 0)
if (funcs.Count == 0) return EasyValueTask.CompletedTask;
return InvokeHandlersSequentially(clientChannel, e, funcs);
}
private static ValueTask InvokeHandlersSequentially(
IClientChannel clientChannel, ReceivedDataEventArgs e, ChannelReceivedEventHandler funcs)
{
var enumerator = new HandlerEnumerator(clientChannel, e, funcs);
return enumerator.MoveNextAsync();
}
private struct HandlerEnumerator
{
private readonly IClientChannel _channel;
private readonly ReceivedDataEventArgs _e;
private readonly ChannelReceivedEventHandler _funcs;
private int _index;
public HandlerEnumerator(IClientChannel channel, ReceivedDataEventArgs e, ChannelReceivedEventHandler funcs)
{
for (int i = 0; i < funcs.Count; i++)
_channel = channel;
_e = e;
_funcs = funcs;
_index = -1;
}
public ValueTask MoveNextAsync()
{
_index++;
if (_index >= _funcs.Count) return default;
var func = _funcs[_index];
if (func == null) return MoveNextAsync();
bool isLast = _index == _funcs.Count - 1;
var vt = func.Invoke(_channel, _e, isLast);
if (vt.IsCompletedSuccessfully)
{
var func = funcs[i];
if (func == null) continue;
await func.Invoke(clientChannel, e, i == funcs.Count - 1).ConfigureAwait(false);
if (e.Handled)
{
break;
}
if (_e.Handled) return default;
return MoveNextAsync();
}
return Awaited(vt);
}
private async ValueTask Awaited(ValueTask vt)
{
await vt.ConfigureAwait(false);
if (!_e.Handled)
await MoveNextAsync().ConfigureAwait(false);
}
}
@@ -53,7 +94,7 @@ public static class ChannelOptionsExtensions
/// <param name="clientChannel">通道</param>
/// <param name="funcs">事件</param>
/// <returns></returns>
internal static async Task OnChannelEvent(this IClientChannel clientChannel, ChannelEventHandler funcs)
internal static async ValueTask OnChannelEvent(this IClientChannel clientChannel, ChannelEventHandler funcs)
{
try
{

View File

@@ -65,7 +65,7 @@ public interface IChannel : ISetupConfigObject, IDisposable, IClosableClient, IC
/// <summary>
/// 接收事件回调类
/// </summary>
public class ChannelReceivedEventHandler : List<Func<IClientChannel, ReceivedDataEventArgs, bool, Task>>
public class ChannelReceivedEventHandler : List<Func<IClientChannel, ReceivedDataEventArgs, bool, ValueTask>>
{
}

View File

@@ -131,10 +131,10 @@ public class OtherChannel : SetupConfigObject, IClientChannel
m_dataHandlingAdapter = adapter;
}
private Task PrivateHandleReceivedData(ReadOnlyMemory<byte> byteBlock, IRequestInfo requestInfo)
private async Task PrivateHandleReceivedData(ReadOnlyMemory<byte> byteBlock, IRequestInfo requestInfo)
{
LastReceivedTime = DateTime.Now;
return this.OnChannelReceivedEvent(new ReceivedDataEventArgs(byteBlock, requestInfo), ChannelReceived);
await this.OnChannelReceivedEvent(new ReceivedDataEventArgs(byteBlock, requestInfo), ChannelReceived).ConfigureAwait(false);
}
/// <summary>

View File

@@ -81,8 +81,8 @@ public class DeviceSingleStreamDataHandleAdapter<TRequest> : CustomDataHandlingA
request = Request == null ? Request = GetInstance() : Request;
else
{
if (!beCached)
request = GetInstance();
//if (!beCached)
request = GetInstance();
}
var pos = byteBlock.BytesRead;

View File

@@ -308,7 +308,7 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
/// <summary>
/// 接收,非主动发送的情况,重写实现非主从并发通讯协议,如果通道存在其他设备并且不希望其他设备处理时,设置<see cref="TouchSocketEventArgs.Handled"/> 为true
/// </summary>
protected virtual Task ChannelReceived(IClientChannel client, ReceivedDataEventArgs e, bool last)
protected virtual ValueTask ChannelReceived(IClientChannel client, ReceivedDataEventArgs e, bool last)
{
if (e.RequestInfo is MessageBase response)
{
@@ -325,7 +325,7 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
}
}
return EasyTask.CompletedTask;
return EasyValueTask.CompletedTask;
}
public bool AutoConnect { get; protected set; } = true;
/// <inheritdoc/>
@@ -583,8 +583,16 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
else
{
var operResult = waitData.Check(reusableTimeout.TimeoutStatus);
waitData.CompletedData.ErrorMessage = $"{operResult.ErrorMessage}, sign: {sign}";
return waitData.CompletedData;
if(waitData.CompletedData!=null)
{
waitData.CompletedData.ErrorMessage = $"{operResult.ErrorMessage}, sign: {sign}";
return waitData.CompletedData;
}
else
{
return new MessageBase(new OperationCanceledException());
}
//return new MessageBase(operResult) { ErrorMessage = $"{operResult.ErrorMessage}, sign: {sign}" };
}
}

View File

@@ -23,7 +23,7 @@ namespace ThingsGateway.Foundation;
/// </summary>
public class JsonLocalizer : IStringLocalizer
{
private readonly ConcurrentDictionary<string, JObject> _resources = new();
private readonly NonBlockingDictionary<string, JObject> _resources = new();
private string _folderName;
private Type _type;

View File

@@ -107,7 +107,8 @@ public class CronScheduledTask : DisposeBase, IScheduledTask
{
if (!Check())
{
SetNext(next);
int nextValue = next;
SetNext(nextValue);
}
}
}
@@ -149,7 +150,8 @@ public class CronScheduledTask : DisposeBase, IScheduledTask
{
if (!Check())
{
SetNext(next);
int nextValue = next;
SetNext(nextValue);
}
}
}

View File

@@ -93,7 +93,8 @@ public class ScheduledAsyncTask : DisposeBase, IScheduledTask, IScheduledIntInte
{
if (!Check() && IntervalMS > 8)
{
SetNext(next);
int nextValue = next;
SetNext(nextValue);
}
}
}

View File

@@ -80,7 +80,8 @@ public class ScheduledSyncTask : DisposeBase, IScheduledTask, IScheduledIntInter
{
if (!Check() && IntervalMS > 8)
{
SetNext(next);
int nextValue = next;
SetNext(nextValue);
}
}
}

Some files were not shown because too many files have changed in this diff Show More