如何在IP探测任务成功连接后终止任务或使用取消令牌?
IP探测任务成功时终止所有任务的实现方案
我有一个IP探测方法,通过TcpClient测试每个IP地址,当任意IP连接成功时,我会在内存中设置WebServerIp,此时任务即视为成功完成。请问能否在我的现有代码中实现该逻辑?或者有没有办法在任务“成功”时使用取消令牌?
原始代码
public async Task<bool> DiscoverDeviceAsync() { var ips = GeneralService.GetIPAddress(); PhoneIp = string.Join(",", ips.Select(x => x.ToString())); await Task.Delay(1000); var tasks = new List<Task>(); foreach (IPAddress ip in ips) { byte[] ipBytes = ip.GetAddressBytes(); for (int i = 0; i <= 254; i++) { ipBytes[3] = (byte)i; IPAddress currentIPAddress = new(ipBytes); tasks.Add(ScanIPAddress(currentIPAddress, 9431)); } } await Task.WhenAny(tasks); return WebServerIp != ""; } async Task ScanIPAddress(IPAddress ipAddress, int port) { try { using TcpClient tcpClient = new() { SendTimeout = 100, ReceiveTimeout = 100, }; await tcpClient.ConnectAsync(ipAddress, port); WebServerIp = ipAddress.ToString(); } catch { throw new Exception($"No device found at address: {ipAddress}"); } }
现有代码问题分析
ScanIPAddress捕获异常后直接抛出,可能导致Task.WhenAny优先捕获失败任务,而非等待成功任务完成- 无取消机制,找到目标IP后其他扫描任务仍会继续执行,浪费系统资源
Task.Delay(1000)属于无意义延迟,建议移除
修改后的实现(带取消令牌)
private CancellationTokenSource _cts; public string WebServerIp { get; private set; } = string.Empty; public async Task<bool> DiscoverDeviceAsync() { WebServerIp = string.Empty; var ips = GeneralService.GetIPAddress(); PhoneIp = string.Join(",", ips.Select(x => x.ToString())); _cts = new CancellationTokenSource(); var tasks = new List<Task>(); foreach (IPAddress ip in ips) { byte[] ipBytes = ip.GetAddressBytes(); for (int i = 0; i <= 254; i++) { ipBytes[3] = (byte)i; IPAddress currentIPAddress = new(ipBytes); tasks.Add(ScanIPAddress(currentIPAddress, 9431, _cts.Token)); } } try { var completedTask = await Task.WhenAny(tasks); // 如果任务成功完成,触发取消终止其他任务 if (completedTask.Status == TaskStatus.RanToCompletion) { _cts.Cancel(); } } finally { // 等待所有任务结束(忽略取消异常),避免后台任务残留 await Task.WhenAll(tasks.Select(t => t.ContinueWith(_ => { }))); _cts.Dispose(); } return !string.IsNullOrEmpty(WebServerIp); } async Task ScanIPAddress(IPAddress ipAddress, int port, CancellationToken token) { // 先检查令牌状态,避免无效操作 token.ThrowIfCancellationRequested(); try { using TcpClient tcpClient = new() { SendTimeout = 100, ReceiveTimeout = 100, }; // 使用支持取消的ConnectAsync重载 await tcpClient.ConnectAsync(ipAddress, port, token); // 连接成功后设置IP并触发取消 WebServerIp = ipAddress.ToString(); _cts.Cancel(); } catch (OperationCanceledException) { // 取消异常无需处理 } catch (Exception) { // 连接失败异常直接忽略,不向外抛出 } }
关键改动说明
- 新增
CancellationTokenSource,用于在成功时终止所有剩余扫描任务 ScanIPAddress接收取消令牌,在方法开头检查令牌状态,避免无效操作;连接时使用支持取消的ConnectAsync重载- 连接成功后立即触发取消,终止其他任务
- 在finally块中等待所有任务结束,确保资源被正确清理,避免后台任务泄漏
- 移除无意义的延迟,不再抛出连接失败异常,避免干扰
Task.WhenAny的判断
内容的提问来源于stack exchange,提问作者Hickori
相关产品推荐
相关产品推荐

