黄中银 1 день назад
Родитель
Сommit
ccd1ca3d49

+ 23 - 0
Apq.Cfg/ICfgRoot.cs

@@ -46,6 +46,29 @@ public interface ICfgRoot : IDisposable, IAsyncDisposable
     /// <returns>键值对字典</returns>
     IReadOnlyDictionary<string, T?> GetMany<T>(IEnumerable<string> keys);
 
+    /// <summary>
+    /// 高性能批量获取:通过回调方式返回结果,零堆分配
+    /// </summary>
+    /// <param name="keys">要获取的键集合</param>
+    /// <param name="onValue">每个键值对的回调处理函数</param>
+    /// <remarks>
+    /// 此方法避免了 Dictionary 分配开销,适合高频调用场景。
+    /// 回调会按键的顺序依次调用。
+    /// </remarks>
+    void GetMany(IEnumerable<string> keys, Action<string, string?> onValue);
+
+    /// <summary>
+    /// 高性能批量获取:通过回调方式返回结果并转换类型,零堆分配
+    /// </summary>
+    /// <typeparam name="T">目标类型</typeparam>
+    /// <param name="keys">要获取的键集合</param>
+    /// <param name="onValue">每个键值对的回调处理函数</param>
+    /// <remarks>
+    /// 此方法避免了 Dictionary 分配开销,适合高频调用场景。
+    /// 回调会按键的顺序依次调用。
+    /// </remarks>
+    void GetMany<T>(IEnumerable<string> keys, Action<string, T?> onValue);
+
     /// <summary>
     /// 批量设置多个配置值,减少锁竞争
     /// </summary>

+ 61 - 0
Apq.Cfg/MergedCfgRoot.cs

@@ -231,6 +231,67 @@ internal sealed class MergedCfgRoot : ICfgRoot
         return result;
     }
 
+    public void GetMany(IEnumerable<string> keys, Action<string, string?> onValue)
+    {
+        // Lazy 策略:访问前确保配置是最新的
+        _coordinator?.EnsureLatest();
+
+        foreach (var key in keys)
+        {
+            string? value = null;
+            var found = false;
+
+            // 先检查 Pending
+            foreach (var level in _levelsDescending)
+            {
+                if (_levelData[level].Pending.TryGetValue(key, out var pendingValue))
+                {
+                    value = pendingValue;
+                    found = true;
+                    break;
+                }
+            }
+
+            if (!found)
+            {
+                value = _merged[key];
+            }
+
+            onValue(key, value);
+        }
+    }
+
+    public void GetMany<T>(IEnumerable<string> keys, Action<string, T?> onValue)
+    {
+        // Lazy 策略:访问前确保配置是最新的
+        _coordinator?.EnsureLatest();
+
+        foreach (var key in keys)
+        {
+            string? rawValue = null;
+            var found = false;
+
+            // 先检查 Pending
+            foreach (var level in _levelsDescending)
+            {
+                if (_levelData[level].Pending.TryGetValue(key, out var pendingValue))
+                {
+                    rawValue = pendingValue;
+                    found = true;
+                    break;
+                }
+            }
+
+            if (!found)
+            {
+                rawValue = _merged[key];
+            }
+
+            T? value = rawValue == null ? default : ValueConverter.Convert<T>(rawValue);
+            onValue(key, value);
+        }
+    }
+
     public async Task SaveAsync(int? targetLevel = null, CancellationToken cancellationToken = default)
     {
         var level = targetLevel ?? (_levelsDescending.Length > 0 ? _levelsDescending[0] : throw new InvalidOperationException("没有配置源"));

+ 34 - 0
benchmarks/Apq.Cfg.Benchmarks/BatchOperationBenchmarks.cs

@@ -143,6 +143,40 @@ public class BatchOperationBenchmarks : IDisposable
 
     #endregion
 
+    #region GetMany 回调方式(高性能)
+
+    /// <summary>
+    /// 使用 GetMany 回调方式批量获取 10 个键(零分配)
+    /// </summary>
+    [Benchmark]
+    [BenchmarkCategory("GetManyCallback")]
+    public void GetMany_Callback_10Keys()
+    {
+        _cfg.GetMany(_keys10, (key, value) => { _ = value; });
+    }
+
+    /// <summary>
+    /// 使用 GetMany 回调方式批量获取 50 个键(零分配)
+    /// </summary>
+    [Benchmark]
+    [BenchmarkCategory("GetManyCallback")]
+    public void GetMany_Callback_50Keys()
+    {
+        _cfg.GetMany(_keys50, (key, value) => { _ = value; });
+    }
+
+    /// <summary>
+    /// 使用 GetMany 回调方式批量获取 100 个键(零分配)
+    /// </summary>
+    [Benchmark]
+    [BenchmarkCategory("GetManyCallback")]
+    public void GetMany_Callback_100Keys()
+    {
+        _cfg.GetMany(_keys100, (key, value) => { _ = value; });
+    }
+
+    #endregion
+
     #region GetMany<T> 类型转换批量获取
 
     /// <summary>