WPF в конечном итоге зависает даже при сканировании портов из асинхронных задач.C#

Место общения программистов C#
Ответить Пред. темаСлед. тема
Anonymous
 WPF в конечном итоге зависает даже при сканировании портов из асинхронных задач.

Сообщение Anonymous »

Я пишу сканер портов и не могу создать гибкий пользовательский интерфейс.
У меня есть DataGrid, который отображает результаты сканирования: Тогда у меня есть объект, содержащий результат сканирования.

Код: Выделить всё

public class NetScanResult : ObservableObject
{
private IPAddress ipAddress;
public IPAddress IpAddress
{
get => ipAddress;
set
{
SetProperty(ref ipAddress, value);
OnPropertyChanged(nameof(IpAddressStr));
}
}

private ObservableCollection openPorts;
public ObservableCollection OpenPorts
{
get => openPorts;
set { SetProperty(ref openPorts, value); }
}

public NetScanResult()
{
OpenPorts = new ObservableCollection();
}
}
И, наконец, у меня есть логика сканирования:

Код: Выделить всё

[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(netScanResults);
var tasks = new List();

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}");
}
}
Я запускается плавно, но через некоторое время пользовательский интерфейс все еще зависает, и я не понимаю, почему.
Он размораживается, когда все потоки сканирования завершены.
Чего мне не хватает?
Обновление:
Я изменил свой код и реализовал ваши предложения.

Код: Выделить всё

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 addresses = await NetUtility.ScanNetwork2_Prepare(NetScanIpRangeBegin, NetScanIpRangeEnd, NetScanNetmaskSelected);

//
var list = new List();

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(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();

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;
}
Я реализовал таймер для отображения прогресса, а также упростил код сканирования портов.
Добавление семафора для ограничения количества одновременных задач сканирования до 5 и переход на Dispatcher.BeginInvoke для обновлений пользовательского интерфейса несколько уменьшил проблемы зависания пользовательского интерфейса, но не полностью. Однако это также значительно увеличило время, необходимое для завершения всех сканирований.
Я также вижу много исключений: «System.Net.Sockets.SocketException» в System.Net. .Sockets.dll и исключение: «System.Net.Sockets.SocketException» в System.Private.CoreLib.dll в выводе отладки, что странно, поскольку есть try-catch заблокируйте метод ConnectAsync — я подумал, что, возможно, эти исключения также могут замедлить работу приложения.
Если я удалю семафор или увеличу его ограничение с 5 до 7 или большего числа, пользовательский интерфейс практически не отвечает на запросы, но иногда ему удается отображать некоторые обновления.
Я запускаю это на процессоре Intel i5-1135g7, если это имеет значение.
Все еще пытаюсь понять причину зависаний пользовательского интерфейса.


Подробнее здесь: https://stackoverflow.com/questions/789 ... sync-tasks
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «C#»