WPF端口扫描器异步任务执行时UI仍冻结问题排查
WPF端口扫描器UI卡顿问题排查
开发WPF端口扫描器时,始终无法实现流畅的UI体验:程序启动时流畅,但运行一段时间后UI会冻结,直到所有扫描线程结束才恢复。
相关代码片段
展示扫描结果的DataGrid
<DataGrid Grid.Column="0" ItemsSource="{Binding NetScanResults}" Margin="5" AutoGenerateColumns="False" IsReadOnly="True">
扫描结果实体类
public class NetScanResult : ObservableObject { private IPAddress ipAddress; public IPAddress IpAddress { get => ipAddress; set { SetProperty(ref ipAddress, value); OnPropertyChanged(nameof(IpAddressStr)); } } private ObservableCollection<int> openPorts; public ObservableCollection<int> OpenPorts { get => openPorts; set { SetProperty(ref openPorts, value); } } public NetScanResult() { OpenPorts = new ObservableCollection<int>(); } }
初始扫描逻辑
[RelayCommand] public async void NetScan2Start() { try { int[] ports = { 22, 80, 443, 554, 3389, 8000, 37777 }; int timeoutMs = 2500; var stopwatch = new Stopwatch(); stopwatch.Start(); var netScanResults = await NetUtility.ScanNetwork2_Prepare(NetScanIpRangeBegin, NetScanIpRangeEnd, NetScanNetmaskSelected, ports, timeoutMs); NetScanResults = new ObservableCollection<NetScanResult>(netScanResults); var tasks = new List<Task>(); foreach (var netScanResult in NetScanResults) { foreach (var port in ports) { NetScanResult tempResult = netScanResult; int tempPort = port; var task = ScanPortAsync(tempResult, tempPort, timeoutMs); tasks.Add(task); } } await Task.WhenAll(tasks.ToArray()); stopwatch.Stop(); MessageBox.Show($"Scan finished in {stopwatch.ElapsedMilliseconds}ms"); } catch (Exception ex) { ShowInvalidData(ex.Message); return; } } private async Task ScanPortAsync(NetScanResult result, int port, int timeoutMs) { try { Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp); var connectResult = socket.BeginConnect(result.IpAddress, port, null, null); await Task.Run(() => { connectResult.AsyncWaitHandle.WaitOne(timeoutMs, true); }); if (socket.Connected) { socket.EndConnect(connectResult); Application.Current.Dispatcher.Invoke(() => { result.OpenPorts.Add(port); }); Debug.WriteLine($"{result.IpAddress}:{port} - open."); } socket.Close(); } catch (Exception ex) { Debug.WriteLine($" Exception happened =( - {ex.Message}"); } }
更新后的改进与新问题
已按建议修改代码:添加进度显示计时器、简化端口扫描逻辑,引入SemaphoreSlim限制并发任务数为5,改用Dispatcher.BeginInvoke更新UI。但UI冻结问题仅缓解未彻底解决,同时扫描耗时大幅增加。
调试输出频繁出现以下异常:Exception thrown: 'System.Net.Sockets.SocketException' in System.Net.Sockets.dllException thrown: 'System.Net.Sockets.SocketException' in System.Private.CoreLib.dll
尽管ConnectAsync已被try-catch包裹,怀疑这些异常会拖慢程序。若移除信号量或把并发数提高到7及以上,UI几乎无响应,仅偶尔能显示更新。设备CPU为Intel i5-1135g7,仍在寻找UI冻结根源。
更新后的扫描逻辑代码
private int progressStep; private int progress; private int progressTotal; private DispatcherTimer progressTimer; [RelayCommand] public async void NetScan3Start() => await Task.Run(async () => { int[] ports = { 22, 80, 443, 554, 3389, 8000, 37777 }; int timeoutMs = 250; var stopwatch = new Stopwatch(); stopwatch.Start(); IList<IPAddress> addresses = await NetUtility.ScanNetwork2_Prepare(NetScanIpRangeBegin, NetScanIpRangeEnd, NetScanNetmaskSelected); // var list = new List<NetScanResult2>(); foreach (var address in addresses) { list.Add(new NetScanResult2(address)); } progressTotal = list.Count * ports.Length; progressStep = (int)(list.Count * ports.Length * 0.05); //5% of total amount; progress = 0; _ = Application.Current.Dispatcher.BeginInvoke(new Action(() => { NetScanResults = new ObservableCollection<NetScanResult2>(list); })); progressTimer = new DispatcherTimer(DispatcherPriority.Normal, Application.Current.Dispatcher); progressTimer.Interval = TimeSpan.FromSeconds(1); progressTimer.Tick += ProgressTimer_Tick; progressTimer.Start(); Application.Current.Dispatcher.Thread.Priority = ThreadPriority.Highest; // var scanTasks = new List<Task>(); var semaphore = new SemaphoreSlim(5); foreach (var netScanResult in list) { var tmpNetScanResult = netScanResult; var tmpAddress = netScanResult.IpAddress; foreach (var port in ports) { var tmpPort = port; var scanTask = Task.Run(async () => { await semaphore.WaitAsync(); try { var client = new TcpClient(); var connectTask = client.ConnectAsync(tmpAddress, tmpPort); var delayTask = Task.Delay(timeoutMs); var result = await Task.WhenAny(connectTask, delayTask); if (result == connectTask && client.Connected) { _ = Application.Current.Dispatcher.BeginInvoke(new Action(() => { tmpNetScanResult.OpenPorts.Add(tmpPort); })); } client.Dispose(); } catch (SocketException) { } catch (Exception ex) { } finally { ++progress; semaphore.Release(); } }); scanTasks.Add(scanTask); } } await Task.WhenAll(scanTasks); stopwatch.Stop(); progressTimer.Stop(); MessageBox.Show($"Finished in {stopwatch.ElapsedMilliseconds}ms"); }); private void ProgressTimer_Tick(object? sender, EventArgs e) { NetScanAmount = progressTotal - progress; }
内容的提问来源于stack exchange,提问作者CorellianAle
相关产品推荐
相关产品推荐

