Dispose时同步事件处理器:避免WebSocket异常的解决方案
解决DevToolsSession释放时后台线程调用API的WebSocket异常问题
你的问题核心是DevToolsSession释放过程中,后台线程的RequestPaused事件处理器仍在执行,此时调用ContinueRequestWithoutModification会因为WebSocket已进入关闭状态而抛出异常。即使提前注销事件处理器,也无法阻止已经启动的异步逻辑继续执行。下面给你几个简洁可靠的解决方案:
方案1:先禁用监听,等待所有请求处理完成再释放
这是最稳妥的方式,从根源上切断新事件、等待已有任务完成后再释放Session:
[Test, Repeat(20)] public void EventHandlersCanRunAfterDevToolsIsDisposed() { using var driver = new ChromeDriver(); var caughtExceptions = new List<Exception>(); // 用并发集合跟踪正在处理的请求任务 var activeTasks = new ConcurrentBag<Task>(); using (var devToolsSession = driver.GetDevToolsSession(new DevToolsOptions { ProtocolVersion = 127 })) { var networkAdapter = new NetworkAdapter(devToolsSession); var fetch = new FetchAdapter(devToolsSession); var network = new V127Network(networkAdapter, fetch); var enableCommandSettings = new EnableCommandSettings { Patterns = new RequestPattern[] { new() { RequestStage = RequestStage.Request, UrlPattern = "*" } } }; fetch.Enable(enableCommandSettings); network.RequestPaused += async (_, args) => { var task = Task.Run(async () => { try { // Do some stuff... await network.ContinueRequestWithoutModification(args.RequestData); } catch (Exception ex) { lock (caughtExceptions) { caughtExceptions.Add(ex); } } }); activeTasks.Add(task); }; driver.Url = "https://www.google.com"; // 关键步骤:先禁用Fetch监听,停止接收新事件 fetch.Disable(); // 等待所有正在处理的请求任务完成 Task.WaitAll(activeTasks.ToArray()); } // 现在再释放DevToolsSession就安全了 if (caughtExceptions.Count != 0) throw new AggregateException(caughtExceptions); }
方案2:用CancellationToken控制异步操作
通过令牌在Session释放时取消未完成的操作,避免无效的API调用:
[Test, Repeat(20)] public void EventHandlersCanRunAfterDevToolsIsDisposed() { using var driver = new ChromeDriver(); var caughtExceptions = new List<Exception>(); using var cts = new CancellationTokenSource(); using (var devToolsSession = driver.GetDevToolsSession(new DevToolsOptions { ProtocolVersion = 127 })) { var networkAdapter = new NetworkAdapter(devToolsSession); var fetch = new FetchAdapter(devToolsSession); var network = new V127Network(networkAdapter, fetch); var enableCommandSettings = new EnableCommandSettings { Patterns = new RequestPattern[] { new() { RequestStage = RequestStage.Request, UrlPattern = "*" } } }; fetch.Enable(enableCommandSettings); network.RequestPaused += async (_, args) => { try { // 先检查令牌是否已取消,避免执行后续操作 cts.Token.ThrowIfCancellationRequested(); // Do some stuff... await network.ContinueRequestWithoutModification(args.RequestData); } catch (OperationCanceledException) { // 取消操作无需记录异常 } catch (Exception ex) { lock (caughtExceptions) { caughtExceptions.Add(ex); } } }; driver.Url = "https://www.google.com"; } // 释放Session时触发取消 cts.Cancel(); if (caughtExceptions.Count != 0) throw new AggregateException(caughtExceptions); }
方案3:兜底捕获特定异常(应急可用)
如果上面的方案仍有遗漏,可以在调用API时捕获WebSocketException并判断状态,忽略因Session关闭导致的异常:
network.RequestPaused += async (_, args) => { try { // Do some stuff... await network.ContinueRequestWithoutModification(args.RequestData); } catch (WebSocketException ex) when (ex.Message.Contains("CloseSent")) { // 忽略因Session关闭导致的无效调用异常 } catch (Exception ex) { lock (caughtExceptions) { caughtExceptions.Add(ex); } } };
优先推荐方案1,它从根源上避免了在Session关闭后执行API调用的情况,保证了线程安全。
内容的提问来源于stack exchange,提问作者Arnon Axelrod
相关产品推荐
相关产品推荐

