Unity+SignalR场景下HttpRequestException导致HoloLens帧率骤降问题
问题:HoloLens客户端SignalR断开重连时帧率骤降
问题场景
使用ASP.NET Core Web API服务器 + Unity/HoloLens客户端,正常连接运行无异常。测试断开重连流程:启动服务器→客户端连接→关闭服务器。断开后WithAutomaticReconnect定义的逻辑触发,随后连接关闭;在Closed方法中调用自定义Action触发Restart操作,此时StartAsync抛出HttpRequestException(符合预期,因服务器离线),但HoloLens应用帧率从60FPS骤降至10FPS,持续约20秒后恢复。
错误信息
Default: ⨯[SignalRClient]: System.Net.Http.HttpRequestException: An error occurred while sending the request System.Net.WebException: Error: ConnectFailure (No connection could be established because the target computer refused to connect. UnityEngine.DebugLogHandler:LogFormat (UnityEngine.LogType,UnityEngine.Object,string,object[]) MyLogHandler:LogFormat (UnityEngine.LogType,UnityEngine.Object,string,object[]) (at Assets/Scripts/Logging/MyLogHandler.cs:29) UnityEngine.Logger:LogError (string,object) MyApp.Logging.MyLogger:DoLog (MyApp.Logging.DebugMode,System.Action`2<string, object>,string,object,object[]) (at Assets/Scripts/Logging/MyLogger.cs:66) MyApp.Logging.MyLogger:LogError (MyApp.Logging.DebugMode,object,object[]) (at Assets/Scripts/Logging/MyLogger.cs:33) SignalRClient/<StartConnection>d__4:MoveNext () (at Assets/Scripts/Clients/SignalRClient.cs:78) System.Runtime.CompilerServices.AsyncTaskMethodBuilder:SetException (System.Exception) Microsoft.AspNetCore.SignalR.Client.HubConnection/<StartAsync>d__48:MoveNext () System.Runtime.CompilerServices.AsyncTaskMethodBuilder:SetException (System.Exception) Microsoft.AspNetCore.SignalR.Client.HubConnection/<StartAsyncInner>d__49:MoveNext () System.Runtime.CompilerServices.AsyncTaskMethodBuilder:SetException (System.Exception) Microsoft.AspNetCore.SignalR.Client.HubConnection/<StartAsyncCore>d__58:MoveNext () System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder`1<Microsoft.AspNetCore.Connections.ConnectionContext>:SetException (System.Exception) Microsoft.AspNetCore.Http.Connections.Client.HttpConnectionFactory/d__3:MoveNext () System.Runtime.CompilerServices.AsyncTaskMethodBuilder:SetException (System.Exception) Microsoft.AspNetCore.Http.Connections.Client.HttpConnection/ d__40:MoveNext () System.Runtime.CompilerServices.AsyncTaskMethodBuilder:SetException (System.Exception) Microsoft.AspNetCore.Http.Connections.Client.HttpConnection/d__41:MoveNext () System.Runtime.CompilerServices.AsyncTaskMethodBuilder:SetException (System.Exception) Microsoft.AspNetCore.Http.Connections.Client.HttpConnection/d__44:MoveNext () System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<Microsoft.AspNetCore.Http.Connections.NegotiationResponse>:SetException (System.Exception) Microsoft.AspNetCore.Http.Connections.Client.HttpConnection/<GetNegotiationResponseAsync>d__52:MoveNext () System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<Microsoft.AspNetCore.Http.Connections.NegotiationResponse>:SetException (System.Exception) Microsoft.AspNetCore.Http.Connections.Client.HttpConnection/d__45:MoveNext () System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<System.Net.Http.HttpResponseMessage>:SetException (System.Exception) Microsoft.AspNetCore.Http.Connections.Client.Internal.LoggingHttpMessageHandler/<SendAsync>d__2:MoveNext () System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<System.Net.Http.HttpResponseMessage>:SetException (System.Exception) Microsoft.AspNetCore.Http.Connections.Client.Internal.AccessTokenHttpMessageHandler/d__3:MoveNext () System.Threading.Tasks.TaskFactory`1<System.Net.WebResponse>:FromAsyncCoreLogic (System.IAsyncR
代码实现
public class SignalRClient { private Action _connectionClosed; public SignalRClient() { _connectionClosed += Restart; _hubConnection = new HubConnectionBuilder() .WithUrl(ConfigModel.Config.MyUri + "/hmdHub") .WithAutomaticReconnect(new[]{ TimeSpan.FromSeconds(5) }) .Build(); _hubConnection.Reconnecting += Reconnecting; _hubConnection.Reconnected += Reconnected; _hubConnection.Closed += Closed; } public async void Start() { ConfigureConnection(); _ = await StartConnection(); } public async void Restart() => _ = await StartConnection(); public async Task Dispose() { _connectionClosed -= Restart; _hubConnection.Reconnecting -= Reconnecting; _hubConnection.Reconnected -= Reconnected; _hubConnection.Closed -= Closed; await _hubConnection.StopAsync(); await _hubConnection.DisposeAsync(); } private void ConfigureConnection() { _hubConnection.On<Alarm>("CreateAlarm", (alarmObject) => { MyLogger.Log(DebugMode.Default, "SignalRClient>CreateAlarm", $"Got new alarm with id: {alarmObject.Id} with message: {alarmObject.Message}"); }); } private async Task<bool> StartConnection() { _tokenSource = new CancellationTokenSource(); while (true) { try { await _hubConnection.StartAsync(_tokenSource.Token); MyLogger.LogSuccess(DebugMode.Default, this, "Connected to SignalR-Hub"); return true; } catch when (_tokenSource.IsCancellationRequested) { return false; } catch(Exception ex) { MyLogger.LogError(DebugMode.Default, this, ex.ToString()); await Task.Delay(5000); } } } private Task Reconnecting(Exception arg) { MyLogger.Log(DebugMode.Default, this, "Attempting to reconnect..."); return Task.CompletedTask; } private Task Reconnected(string arg) { MyLogger.LogSuccess(DebugMode.Default, this, "Connection restored."); return Task.CompletedTask; } private Task Closed(Exception arg) { MyLogger.LogWarning(DebugMode.Default, this, "Connection closed."); _connectionClosed?.Invoke(); return Task.CompletedTask; } private HubConnection _hubConnection; private CancellationTokenSource _tokenSource; }
原因分析
- 双重重连逻辑冲突:同时启用了SignalR内置的
WithAutomaticReconnect和手动触发的Restart重连循环,两个独立的重连任务同时发起网络请求,占用HoloLens有限的CPU和网络资源,引发帧率下降。 - async void滥用:
Start和Restart使用async void,导致异步任务脱离Unity上下文管理,异常无法被正确捕获,还可能造成主线程调度混乱。 - 日志输出过载:每次连接失败都会打印完整的异常堆栈,高频日志输出占用大量CPU资源,进一步拖慢帧率。
解决方案
1. 移除重复重连逻辑
保留SignalR内置的WithAutomaticReconnect即可,它会自动处理断开后的重连流程,无需手动触发:
public SignalRClient() { // 移除手动重连绑定 // _connectionClosed += Restart; _hubConnection = new HubConnectionBuilder() .WithUrl(ConfigModel.Config.MyUri + "/hmdHub") .WithAutomaticReconnect(new[]{ TimeSpan.FromSeconds(5) }) .Build(); _hubConnection.Reconnecting += Reconnecting; _hubConnection.Reconnected += Reconnected; _hubConnection.Closed += Closed; } private Task Closed(Exception arg) { MyLogger.LogWarning(DebugMode.Default, this, "Connection closed."); // 移除手动重连触发 // _connectionClosed?.Invoke(); return Task.CompletedTask; }
2. 替换async void为async Task
Unity中异步方法应使用async Task,避免上下文丢失和异常无法处理:
public async Task Start() { ConfigureConnection(); await StartConnection(); } // 若需手动重启,同步修改为async Task public async Task Restart() => await StartConnection();
调用时在Unity异步上下文执行:
// 在MonoBehaviour脚本中 private async void Start() { await _signalRClient.Start(); }
3. 优化日志输出
简化错误日志,避免每次失败都打印完整堆栈:
catch(Exception ex) { MyLogger.LogError(DebugMode.Default, this, $"连接失败,5秒后重试:{ex.Message}"); await Task.Delay(5000); }
4. 确保重连循环唯一性
若坚持使用手动重连,必须移除SignalR内置重连,避免双重任务:
_hubConnection = new HubConnectionBuilder() .WithUrl(ConfigModel.Config.MyUri + "/hmdHub") // 移除内置重连 // .WithAutomaticReconnect(new[]{ TimeSpan.FromSeconds(5) }) .Build();
内容的提问来源于stack exchange,提问作者Perazim
相关产品推荐
相关产品推荐

