From 93d8174dbea32eceec3dc74c2c2d6fee37a27e2f Mon Sep 17 00:00:00 2001 From: DHR60 <192860629+DHR60@users.noreply.github.com> Date: Sat, 26 Sep 2026 03:08:36 +0000 Subject: [PATCH] Fix async (#10218) * Fix async * Fix async for statistics * Try fix pac async * Fix --- .../Handler/SysProxy/SysProxyHandler.cs | 5 +- v2rayN/ServiceLib/Helper/HttpClientHelper.cs | 10 +- v2rayN/ServiceLib/Helper/SqliteHelper.cs | 27 +++-- v2rayN/ServiceLib/Manager/PacManager.cs | 113 ++++++++---------- v2rayN/ServiceLib/Manager/TaskManager.cs | 12 +- .../Statistics/StatisticsSingboxService.cs | 92 ++++++-------- .../Statistics/StatisticsXrayService.cs | 39 ++++-- .../ViewModels/ClashConnectionsViewModel.cs | 11 +- .../ViewModels/ClashProxiesViewModel.cs | 11 +- 9 files changed, 153 insertions(+), 167 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/SysProxy/SysProxyHandler.cs b/v2rayN/ServiceLib/Handler/SysProxy/SysProxyHandler.cs index e667937a..79847c0a 100644 --- a/v2rayN/ServiceLib/Handler/SysProxy/SysProxyHandler.cs +++ b/v2rayN/ServiceLib/Handler/SysProxy/SysProxyHandler.cs @@ -3,6 +3,7 @@ namespace ServiceLib.Handler.SysProxy; public static class SysProxyHandler { private static readonly string _tag = "SysProxyHandler"; + private static readonly Lazy _pacManager = new(() => new PacManager()); public static async Task UpdateSysProxy(Config config, bool forceDisable) { @@ -56,7 +57,7 @@ public static class SysProxyHandler if (type != ESysProxyType.Pac && Utils.IsWindows()) { - PacManager.Instance.Stop(); + _pacManager.Value.Stop(); } } catch (Exception ex) @@ -110,7 +111,7 @@ public static class SysProxyHandler private static async Task SetWindowsProxyPac(int port) { var portPac = AppManager.Instance.GetLocalPort(EInboundProtocol.pac); - await PacManager.Instance.StartAsync(port, portPac); + await _pacManager.Value.StartAsync(port, portPac); var strProxy = $"{Global.HttpProtocol}{Global.Loopback}:{portPac}/pac?t={DateTime.Now.Ticks}"; ProxySettingWindows.SetProxy(strProxy, "", 4); } diff --git a/v2rayN/ServiceLib/Helper/HttpClientHelper.cs b/v2rayN/ServiceLib/Helper/HttpClientHelper.cs index 9bc38c01..4cbc518e 100644 --- a/v2rayN/ServiceLib/Helper/HttpClientHelper.cs +++ b/v2rayN/ServiceLib/Helper/HttpClientHelper.cs @@ -22,7 +22,7 @@ public class HttpClientHelper this.httpClient = httpClient; } - public async Task TryGetAsync(string url) + public async Task TryGetAsync(string url, CancellationToken cancellationToken = default) { if (url.IsNullOrEmpty()) { @@ -31,8 +31,12 @@ public class HttpClientHelper try { - var response = await httpClient.GetAsync(url); - return await response.Content.ReadAsStringAsync(); + var response = await httpClient.GetAsync(url, cancellationToken); + return await response.Content.ReadAsStringAsync(cancellationToken); + } + catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) + { + throw; } catch { diff --git a/v2rayN/ServiceLib/Helper/SqliteHelper.cs b/v2rayN/ServiceLib/Helper/SqliteHelper.cs index 83c9bf90..82dd60d3 100644 --- a/v2rayN/ServiceLib/Helper/SqliteHelper.cs +++ b/v2rayN/ServiceLib/Helper/SqliteHelper.cs @@ -75,15 +75,28 @@ public sealed class SQLiteHelper public async Task DisposeDbConnectionAsync() { - await Task.Factory.StartNew(() => + await Task.Run(() => { - _db?.Close(); - _db?.Dispose(); - _db = null; + try + { + _db?.Close(); + _db?.Dispose(); + } + finally + { + _db = null; + } - _dbAsync?.GetConnection()?.Close(); - _dbAsync?.GetConnection()?.Dispose(); - _dbAsync = null; + try + { + var conn = _dbAsync?.GetConnection(); + conn?.Close(); + conn?.Dispose(); + } + finally + { + _dbAsync = null; + } }); } } diff --git a/v2rayN/ServiceLib/Manager/PacManager.cs b/v2rayN/ServiceLib/Manager/PacManager.cs index 92c0b5ba..9a45415e 100644 --- a/v2rayN/ServiceLib/Manager/PacManager.cs +++ b/v2rayN/ServiceLib/Manager/PacManager.cs @@ -2,36 +2,37 @@ namespace ServiceLib.Manager; public class PacManager { - private static readonly Lazy _instance = new(() => new PacManager()); - public static PacManager Instance => _instance.Value; - - private int _httpPort; + private const string Tag = "PacManager"; + private CancellationTokenSource? _cts; private int _pacPort; private TcpListener? _tcpListener; - private byte[] _writeContent; - private bool _isRunning; - private bool _needRestart = true; + private byte[] _writeContent = []; public async Task StartAsync(int httpPort, int pacPort) { - _needRestart = httpPort != _httpPort || pacPort != _pacPort || !_isRunning; + var content = await InitText(httpPort); + _writeContent = content; - _httpPort = httpPort; - _pacPort = pacPort; - - await InitText(); - - if (_needRestart) + if (_tcpListener is not null && _pacPort == pacPort) { - Stop(); - RunListener(); + return; } + + Stop(); + var cts = new CancellationTokenSource(); + var listener = TcpListener.Create(pacPort); + listener.Start(); + + _cts = cts; + _pacPort = pacPort; + _tcpListener = listener; + _ = ListenLoopAsync(listener, cts.Token); } - private async Task InitText() + private async Task InitText(int httpPort) { - var customSystemProxyPacPath = AppManager.Instance.Config.SystemProxyItem?.CustomSystemProxyPacPath; - var fileName = (customSystemProxyPacPath.IsNotEmpty() && File.Exists(customSystemProxyPacPath)) + var customSystemProxyPacPath = AppManager.Instance.Config.SystemProxyItem.CustomSystemProxyPacPath; + var fileName = customSystemProxyPacPath.IsNotEmpty() && File.Exists(customSystemProxyPacPath) ? customSystemProxyPacPath : Path.Combine(Utils.GetConfigPath(), "pac.txt"); @@ -45,7 +46,7 @@ public class PacManager } var pacText = await File.ReadAllTextAsync(fileName); - pacText = pacText.Replace("__PROXY__", $"PROXY 127.0.0.1:{_httpPort};DIRECT;"); + pacText = pacText.Replace("__PROXY__", $"PROXY 127.0.0.1:{httpPort};DIRECT;"); var sb = new StringBuilder(); sb.AppendLine("HTTP/1.0 200 OK"); @@ -54,59 +55,43 @@ public class PacManager sb.AppendLine("Content-Length:" + Encoding.UTF8.GetByteCount(pacText)); sb.AppendLine(); sb.Append(pacText); - _writeContent = Encoding.UTF8.GetBytes(sb.ToString()); + return Encoding.UTF8.GetBytes(sb.ToString()); } - private void RunListener() + private async Task ListenLoopAsync(TcpListener listener, CancellationToken token) { - _tcpListener = TcpListener.Create(_pacPort); - _isRunning = true; - _tcpListener.Start(); - Task.Factory.StartNew(async () => + var buffer = new byte[1024]; + try { - while (_isRunning) + while (!token.IsCancellationRequested) { - try - { - if (!_tcpListener.Pending()) - { - await Task.Delay(10); - continue; - } - - var client = await _tcpListener.AcceptTcpClientAsync(); - await Task.Run(() => WriteContent(client)); - } - catch - { - // ignored - } + using var client = await listener.AcceptTcpClientAsync(token).ConfigureAwait(false); + await using var stream = client.GetStream(); + _ = await stream.ReadAsync(buffer, token).ConfigureAwait(false); + await stream.WriteAsync(_writeContent, token).ConfigureAwait(false); + await stream.FlushAsync(token).ConfigureAwait(false); } - }, TaskCreationOptions.LongRunning); - } - - private void WriteContent(TcpClient client) - { - var stream = client.GetStream(); - stream.Write(_writeContent, 0, _writeContent.Length); - stream.Flush(); + } + catch (OperationCanceledException) when (token.IsCancellationRequested) + { + } + catch (Exception ex) + { + Logging.SaveLog(Tag, ex); + } + finally + { + listener.Stop(); + } } public void Stop() { - if (_tcpListener == null) - { - return; - } - try - { - _isRunning = false; - _tcpListener.Stop(); - _tcpListener = null; - } - catch - { - // ignored - } + _cts?.Cancel(); + _tcpListener?.Stop(); + _cts?.Dispose(); + _cts = null; + _pacPort = 0; + _tcpListener = null; } } diff --git a/v2rayN/ServiceLib/Manager/TaskManager.cs b/v2rayN/ServiceLib/Manager/TaskManager.cs index e6fc4b46..dbec401a 100644 --- a/v2rayN/ServiceLib/Manager/TaskManager.cs +++ b/v2rayN/ServiceLib/Manager/TaskManager.cs @@ -12,23 +12,17 @@ public class TaskManager _config = config; _updateFunc = updateFunc; - _ = Task.Factory.StartNew( - ScheduledTasks, - CancellationToken.None, - TaskCreationOptions.LongRunning, - TaskScheduler.Default); + Task.Run(ScheduledTasks); } private async Task ScheduledTasks() { Logging.SaveLog("Setup Scheduled Tasks"); + using var timer = new PeriodicTimer(TimeSpan.FromMinutes(1)); var numOfExecuted = 1; - while (true) + while (await timer.WaitForNextTickAsync().ConfigureAwait(false)) { - //1 minute - await Task.Delay(1000 * 60); - //Execute once 1 minute try { diff --git a/v2rayN/ServiceLib/Services/Statistics/StatisticsSingboxService.cs b/v2rayN/ServiceLib/Services/Statistics/StatisticsSingboxService.cs index 4458529e..60d38412 100644 --- a/v2rayN/ServiceLib/Services/Statistics/StatisticsSingboxService.cs +++ b/v2rayN/ServiceLib/Services/Statistics/StatisticsSingboxService.cs @@ -5,8 +5,7 @@ namespace ServiceLib.Services.Statistics; public class StatisticsSingboxService { private readonly Config _config; - private bool _exitFlag; - private ClientWebSocket? webSocket; + private CancellationTokenSource? _cts; private readonly Func? _updateFunc; private string Url => $"ws://{Global.Loopback}:{AppManager.Instance.StatePort2}/traffic"; private static readonly string _tag = "StatisticsSingboxService"; @@ -15,40 +14,17 @@ public class StatisticsSingboxService { _config = config; _updateFunc = updateFunc; - _exitFlag = false; - _ = Task.Factory.StartNew( - Run, - CancellationToken.None, - TaskCreationOptions.LongRunning, - TaskScheduler.Default); - } - - private async Task Init() - { - await Task.Delay(5000); - - try - { - if (webSocket == null) - { - webSocket = new ClientWebSocket(); - await webSocket.ConnectAsync(new Uri(Url), CancellationToken.None); - } - } - catch { } + Task.Run(Run); } public void Close() { try { - _exitFlag = true; - if (webSocket != null) - { - webSocket.Abort(); - webSocket = null; - } + _cts?.Cancel(); + _cts?.Dispose(); + _cts = null; } catch (Exception ex) { @@ -58,53 +34,63 @@ public class StatisticsSingboxService private async Task Run() { - await Init(); + Close(); + _cts = new CancellationTokenSource(); + var token = _cts.Token; - while (!_exitFlag) + while (!token.IsCancellationRequested) { - await Task.Delay(1000); try { if (!AppManager.Instance.IsRunningCore(ECoreType.sing_box)) { + await Task.Delay(1000, token).ConfigureAwait(false); continue; } - if (webSocket != null) + using var ws = new ClientWebSocket(); + await ws.ConnectAsync(new Uri(Url), token).ConfigureAwait(false); + + var buffer = new byte[1024]; + while (ws.State == WebSocketState.Open + && !token.IsCancellationRequested) { - if (webSocket.State is WebSocketState.Aborted or WebSocketState.Closed) + var res = await ws.ReceiveAsync(new ArraySegment(buffer), token); + if (res.MessageType == WebSocketMessageType.Close) { - webSocket.Abort(); - webSocket = null; - await Init(); - continue; + break; + } + using var ms = new MemoryStream(); + ms.Write(buffer, 0, res.Count); + while (!res.EndOfMessage) + { + res = await ws.ReceiveAsync(new ArraySegment(buffer), token).ConfigureAwait(false); + ms.Write(buffer, 0, res.Count); } - if (webSocket.State != WebSocketState.Open) + var result = Encoding.UTF8.GetString(ms.ToArray()); + if (!result.IsNotEmpty()) { continue; } + ParseOutput(result, out var up, out var down); - var buffer = new byte[1024]; - var res = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - while (!res.CloseStatus.HasValue) + if (_updateFunc != null) { - var result = Encoding.UTF8.GetString(buffer, 0, res.Count); - if (result.IsNotEmpty()) + await _updateFunc.Invoke(new ServerSpeedItem { - ParseOutput(result, out var up, out var down); - - await _updateFunc?.Invoke(new ServerSpeedItem() - { - ProxyUp = (long)(up / 1000), - ProxyDown = (long)(down / 1000) - }); - } - res = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + ProxyUp = (long)(up / 1000), + ProxyDown = (long)(down / 1000), + }).ConfigureAwait(false); } } } + catch (OperationCanceledException) when (token.IsCancellationRequested) + { + break; + } catch { + await Task.Delay(3000, token).ConfigureAwait(false); } } } diff --git a/v2rayN/ServiceLib/Services/Statistics/StatisticsXrayService.cs b/v2rayN/ServiceLib/Services/Statistics/StatisticsXrayService.cs index b7b67e88..90dac33a 100644 --- a/v2rayN/ServiceLib/Services/Statistics/StatisticsXrayService.cs +++ b/v2rayN/ServiceLib/Services/Statistics/StatisticsXrayService.cs @@ -5,33 +5,42 @@ public class StatisticsXrayService private const long linkBase = 1024; private ServerSpeedItem _serverSpeedItem = new(); private readonly Config _config; - private bool _exitFlag; + private CancellationTokenSource? _cts; private readonly Func? _updateFunc; private string Url => $"{Global.HttpProtocol}{Global.Loopback}:{AppManager.Instance.StatePort}/debug/vars"; + private static readonly string _tag = "StatisticsXrayService"; public StatisticsXrayService(Config config, Func updateFunc) { _config = config; _updateFunc = updateFunc; - _exitFlag = false; - _ = Task.Factory.StartNew( - Run, - CancellationToken.None, - TaskCreationOptions.LongRunning, - TaskScheduler.Default); + Task.Run(Run); } public void Close() { - _exitFlag = true; + try + { + _cts?.Cancel(); + _cts?.Dispose(); + _cts = null; + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } } private async Task Run() { - while (!_exitFlag) + Close(); + _cts = new CancellationTokenSource(); + var token = _cts.Token; + + using var timer = new PeriodicTimer(TimeSpan.FromSeconds(1)); + while (await timer.WaitForNextTickAsync(token).ConfigureAwait(false)) { - await Task.Delay(1000); try { if (AppManager.Instance.RunningCoreType != ECoreType.Xray) @@ -39,16 +48,20 @@ public class StatisticsXrayService continue; } - var result = await HttpClientHelper.Instance.TryGetAsync(Url); + var result = await HttpClientHelper.Instance.TryGetAsync(Url, token); if (result != null) { var server = ParseOutput(result) ?? new ServerSpeedItem(); - await _updateFunc?.Invoke(server); + await _updateFunc!.Invoke(server); } } + catch (OperationCanceledException) when (token.IsCancellationRequested) + { + break; + } catch { - // ignored + await Task.Delay(3000, token).ConfigureAwait(false); } } } diff --git a/v2rayN/ServiceLib/ViewModels/ClashConnectionsViewModel.cs b/v2rayN/ServiceLib/ViewModels/ClashConnectionsViewModel.cs index 23454d90..98299fc9 100644 --- a/v2rayN/ServiceLib/ViewModels/ClashConnectionsViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/ClashConnectionsViewModel.cs @@ -30,12 +30,7 @@ public partial class ClashConnectionsViewModel : MyReactiveObject cancelDisposable.DisposeWith(disposables); var token = cancelDisposable.Token; - Task.Factory.StartNew( - async () => await GetClashConnectionsTask(token), - token, - TaskCreationOptions.LongRunning, - TaskScheduler.Default - ); + Task.Run(() => GetClashConnectionsTask(token)); }); } @@ -129,9 +124,9 @@ public partial class ClashConnectionsViewModel : MyReactiveObject try { var numOfExecuted = 1; - while (!token.IsCancellationRequested) + using var timer = new PeriodicTimer(TimeSpan.FromSeconds(1)); + while (await timer.WaitForNextTickAsync(token).ConfigureAwait(false)) { - await Task.Delay(1000, token); numOfExecuted++; if (!(AutoRefresh && AppManager.Instance.ShowInTaskbar && AppManager.Instance.IsRunningCore(ECoreType.sing_box))) diff --git a/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs b/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs index b8c3a096..d3e192c9 100644 --- a/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs @@ -61,12 +61,7 @@ public partial class ClashProxiesViewModel : MyReactiveObject cancelDisposable.DisposeWith(disposables); var token = cancelDisposable.Token; - Task.Factory.StartNew( - async () => await GetClashProxiesTask(token), - token, - TaskCreationOptions.LongRunning, - TaskScheduler.Default - ); + Task.Run(() => GetClashProxiesTask(token)); }); } @@ -113,9 +108,9 @@ public partial class ClashProxiesViewModel : MyReactiveObject try { var numOfExecuted = 1; - while (!token.IsCancellationRequested) + using var timer = new PeriodicTimer(TimeSpan.FromSeconds(1)); + while (await timer.WaitForNextTickAsync(token).ConfigureAwait(false)) { - await Task.Delay(1000, token); numOfExecuted++; if (!(AutoRefresh && AppManager.Instance.ShowInTaskbar && AppManager.Instance.IsRunningCore(ECoreType.sing_box)))