PowerShell设置5985端口检测超时后结果异常的问题
修复Runspace池内快速端口检测的稳定性问题
问题根源分析
你的代码出现重复结果、检测不稳定的核心原因有两个:
- WaitHandle索引错位:使用
[System.Threading.WaitHandle]::WaitAny($jobs.Async.AsyncWaitHandle)时,$jobs列表是动态变化的(处理完的作业会被移除),但WaitAny返回的是调用时传入的WaitHandle数组的索引,当列表元素被移除后,后续循环的索引会对应错误的作业,导致重复处理或遗漏。 - TcpClient资源未释放:直接创建
TcpClient实例但未释放,可能导致套接字资源泄漏,影响后续检测的稳定性。
修复后的完整代码
$ProgressPreference = 'Ignore' $maxThreads = 32 # 创建Runspace池,使用默认会话状态避免交互冲突 $pool = [runspacefactory]::CreateRunspacePool(1, $maxThreads, [initialsessionstate]::CreateDefault2()) $pool.Open() $jobs = [System.Collections.Generic.List[hashtable]]::new() $servers = Get-ADComputer -Filter * -SearchBase "OU=Sales, DC=example,DC=com" | Select-Object -ExpandProperty Name $servers | ForEach-Object { $computer = $_ $instance = [powershell]::Create().AddScript({ param($computer) $portOpen = $false $tcpClient = $null try { $tcpClient = [System.Net.Sockets.TcpClient]::new() # 执行100ms超时的异步连接检测 $portOpen = $tcpClient.ConnectAsync($computer, 5985).Wait(100) } finally { # 强制释放TcpClient资源,避免套接字泄漏 if ($tcpClient) { $tcpClient.Dispose() } } [pscustomobject]@{ Computer = $computer Port = 5985 PortOpen = $portOpen } }).AddParameters(@{ computer = $computer }) $instance.RunspacePool = $pool $jobs.Add(@{ Instance = $instance Async = $instance.BeginInvoke() }) } $result = [System.Collections.Generic.List[pscustomobject]]::new() while ($jobs.Count -gt 0) { # 每次循环生成当前作业的WaitHandle数组副本,确保索引对应正确 $waitHandles = $jobs.Async.AsyncWaitHandle $completedIndex = [System.Threading.WaitHandle]::WaitAny($waitHandles) $completedJob = $jobs[$completedIndex] try { # 收集当前作业的执行结果 $result.AddRange($completedJob.Instance.EndInvoke($completedJob.Async)) } finally { # 释放PowerShell实例资源 $completedJob.Instance.Dispose() # 从列表中移除已完成的作业 $jobs.RemoveAt($completedIndex) } } $pool.Dispose() # 筛选并输出在线服务器 $online = $result | Where-Object PortOpen | Select-Object -ExpandProperty Computer Write-Output $online
关键修复点说明
- 动态生成WaitHandle数组:每次循环都基于当前
$jobs列表生成新的WaitHandle数组,确保WaitAny返回的索引始终对应列表中的正确作业,彻底解决索引错位导致的重复结果问题。 - 强制释放TcpClient资源:通过
try/finally块,无论连接成功与否都释放TcpClient实例,避免套接字资源累积泄漏。 - 高效结果收集:使用
List<pscustomobject>存储结果,比原生数组拼接更高效,同时避免潜在的结果丢失问题。 - 简化Runspace池配置:移除不必要的
$Host参数,使用默认初始化会话状态,减少交互冲突风险。
内容的提问来源于stack exchange,提问作者shawnixer
相关产品推荐
相关产品推荐

