C#中如何在任务取消前通过WebSocket发送Stop_Stream指令
解决方案:确保取消任务时先发送"Stop_Stream"指令再关闭WebSocket
核心思路是让任务自行处理取消后的优雅清理流程,避免外部取消令牌直接中断WebSocket连接。以下是具体修改方案:
1. 重构IPCameraService.Main方法:将清理逻辑整合到取消处理流程中
修改Main方法,在捕获取消异常时优先完成"Stop_Stream"指令发送,再安全关闭WebSocket:
public async Task Main(System.Windows.Controls.Image imageControl, int cameraIndex, CancellationToken token, string cameraUrl) { CameraURLs[cameraIndex] = cameraUrl; try { await StartSocket(cameraIndex, token); await StartStream(cameraIndex, cameraUrl, token); var stopStreamCompletion = new TaskCompletionSource<bool>(); EventHandler(cameraUrl, cameraIndex, token); while (true) { // 每次循环前检查取消令牌,触发清理流程 token.ThrowIfCancellationRequested(); var frame = await RunYolo(cameraIndex, token); if (frame != null) await UpdateDisplay(frame, imageControl, token); } } catch (OperationCanceledException) { // 执行优雅清理:先发送Stop_Stream指令 var stopServerCommand = new { command = "Stop_Stream" }; if (CameraWebsockets.TryGetValue(cameraIndex, out var ws) && ws.State == WebSocketState.Open) { var commandJson = JsonConvert.SerializeObject(stopServerCommand); var commandBytes = Encoding.UTF8.GetBytes(commandJson); // 使用CancellationToken.None确保指令发送不受原取消令牌影响 await ws.SendAsync( new ArraySegment<byte>(commandBytes, 0, commandBytes.Length), WebSocketMessageType.Text, true, CancellationToken.None ); Debug.WriteLine("Stop_Stream指令已发送"); // 主动优雅关闭WebSocket await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "任务取消,优雅关闭", CancellationToken.None); CameraWebsockets.Remove(cameraIndex); Debug.WriteLine("WebSocket已优雅关闭"); } // 重新抛出异常,让调用方感知任务已取消(可选) throw; } }
2. 优化DeviceSettings.IPCameraMethod:确保等待任务完成清理
保留原有取消逻辑,但强化任务等待环节,确保旧任务完成清理后再启动新任务:
private async Task IPCameraMethod() { if (string.IsNullOrWhiteSpace(textBoxUrl.Text) || !int.TryParse(SourceComboBox.Name.Replace("comboBox", ""), out int cameraIndex) || comboBoxSavedCameras.SelectedIndex < 0) { return; } string url = textBoxUrl.Text; int selectedItemIndex = comboBoxSavedCameras.SelectedIndex; if (tokenSources.TryGetValue(cameraIndex, out var tuple)) { if (selectedItemIndex != tuple.Item1) { tuple.Item2.Cancel(); try { await tuple.Item3; // 等待旧任务完成所有清理操作 } catch (OperationCanceledException) { Debug.WriteLine("旧任务已完成取消清理"); } finally { tuple.Item2.Dispose(); tokenSources.Remove(cameraIndex); } } else { return; // 选中项未变化,无需操作 } } // 启动新任务(用Task.Run替代StartNew,更适配异步场景) var newCts = new CancellationTokenSource(); Task newTask = Camera != null ? Task.Run(() => Camera.IPCameraService.Main(SourceImageControl, selectedItemIndex, newCts.Token, url)) : Task.Run(() => (BaseViewModel as AddFaceViewModel).IPCameraService.Main(SourceImageControl, selectedItemIndex, newCts.Token, url)); Debug.WriteLine("New Task Created!"); tokenSources[cameraIndex] = Tuple.Create(selectedItemIndex, newCts, newTask, CameraType.IP); }
关键注意事项
- 避免令牌直接中断WebSocket:发送"Stop_Stream"时使用
CancellationToken.None,确保指令不会因原令牌已取消而失败。 - 检查WebSocket状态:发送指令前确认WebSocket处于
Open状态,避免无效操作。 - 线程安全:如果
CameraWebsockets是多线程访问的集合,建议改用ConcurrentDictionary替代普通Dictionary,避免并发访问异常。 - 取消逻辑统一:确保
StartSocket、StartStream等方法内部不会在令牌取消时直接关闭WebSocket,将关闭逻辑统一到Main方法的清理流程中。
内容的提问来源于stack exchange,提问作者Captain C
相关产品推荐
相关产品推荐

