diff --git a/v2rayN/ServiceLib.Tests/Manager/ProfileExManagerConcurrencyTests.cs b/v2rayN/ServiceLib.Tests/Manager/ProfileExManagerConcurrencyTests.cs index 845da5c3..fd263079 100644 --- a/v2rayN/ServiceLib.Tests/Manager/ProfileExManagerConcurrencyTests.cs +++ b/v2rayN/ServiceLib.Tests/Manager/ProfileExManagerConcurrencyTests.cs @@ -93,6 +93,18 @@ public class ProfileExManagerConcurrencyTests } } + [Test] + public async Task SetSort_FromManyThreads_ShouldTrackMaxSort() + { + var manager = CreateManager(); + await manager.GetMaxSort().Should().BeEqualTo(0); + + Parallel.For(0, 1000, i => manager.SetSort($"sort{i % 100}", i)); + manager.SetTestDelay("unsorted", 100); + + await manager.GetMaxSort().Should().BeEqualTo(999); + } + private static async Task RunConcurrentUpdates(int workers, int indexes, Action update) { using var barrier = new Barrier(workers); diff --git a/v2rayN/ServiceLib/Manager/ProfileExManager.cs b/v2rayN/ServiceLib/Manager/ProfileExManager.cs index cc0f9a74..1c361716 100644 --- a/v2rayN/ServiceLib/Manager/ProfileExManager.cs +++ b/v2rayN/ServiceLib/Manager/ProfileExManager.cs @@ -3,9 +3,10 @@ namespace ServiceLib.Manager; public class ProfileExManager { private static readonly Lazy _instance = new(() => new()); - private ConcurrentBag _lstProfileEx = []; + private ConcurrentDictionary _dicProfileEx = new(); private readonly ConcurrentQueue _queIndexIds = new(); private readonly Lock _profileExLock = new(); + private int _maxSort; public static ProfileExManager Instance => _instance.Value; private static readonly string _tag = "ProfileExHandler"; @@ -19,19 +20,24 @@ public class ProfileExManager await InitData(); } - public async Task> GetProfileExs() + public async Task> GetProfileExs() { - return await Task.FromResult(_lstProfileEx); + return await Task.FromResult(_dicProfileEx.Values); } private async Task InitData() { await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileExItem where indexId not in ( select indexId from ProfileItem )"); - var lstProfileEx = new ConcurrentBag(await SQLiteHelper.Instance.TableAsync().ToListAsync()); + var dicProfileEx = new ConcurrentDictionary(); + foreach (var item in await SQLiteHelper.Instance.TableAsync().ToListAsync()) + { + dicProfileEx[item.IndexId] = item; + } lock (_profileExLock) { - _lstProfileEx = lstProfileEx; + _dicProfileEx = dicProfileEx; + _maxSort = dicProfileEx.Values.Select(t => t.Sort).DefaultIfEmpty(0).Max(); } } @@ -58,8 +64,7 @@ public class ProfileExManager foreach (var id in lstIds) { - var itemNew = _lstProfileEx?.FirstOrDefault(t => t.IndexId == id); - if (itemNew is null) + if (!_dicProfileEx.TryGetValue(id, out var itemNew)) { continue; } @@ -92,23 +97,22 @@ public class ProfileExManager Sort = 0, Message = string.Empty }; - _lstProfileEx.Add(profileEx); + _dicProfileEx[indexId] = profileEx; IndexIdEnqueue(indexId); return profileEx; } private ProfileExItem GetProfileExItem(string? indexId) { - var profileEx = _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId); - if (profileEx is not null) + indexId ??= string.Empty; + if (_dicProfileEx.TryGetValue(indexId, out var profileEx)) { return profileEx; } lock (_profileExLock) { - profileEx = _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId); - return profileEx ?? AddProfileEx(indexId); + return _dicProfileEx.TryGetValue(indexId, out profileEx) ? profileEx : AddProfileEx(indexId); } } @@ -117,7 +121,8 @@ public class ProfileExManager await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileExItem "); lock (_profileExLock) { - _lstProfileEx = []; + _dicProfileEx = new(); + _maxSort = 0; } } @@ -171,24 +176,19 @@ public class ProfileExManager profileEx.Sort = sort; IndexIdEnqueue(indexId); + lock (_profileExLock) + { + _maxSort = Math.Max(_maxSort, sort); + } } public int GetSort(string indexId) { - var profileEx = _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId); - if (profileEx == null) - { - return 0; - } - return profileEx.Sort; + return _dicProfileEx.TryGetValue(indexId, out var profileEx) ? profileEx.Sort : 0; } public int GetMaxSort() { - if (_lstProfileEx.Count <= 0) - { - return 0; - } - return _lstProfileEx.Max(t => t?.Sort ?? 0); + return _maxSort; } }