Странное поведение профилировщика асинхронного кода
У меня есть код, который сканирует сеть по выбранным IP через библиотеку SnmpSharp по протоколам SNMP 1-3 и генерирует события с новыми устройствами.
// Для каждого адреса
for (int i = 0; i < Params.Ip.Length; i++)
{
// Для каждого диапозона
for (int b = Params.FromByte; b <= Params.ToByte; b++)
{
if (_stop) return;
IPEndPoint ip = new IPEndPoint(IPAddress.Parse($"{Params.Ip[i]}.{b}"), Port);
if (Params.V1)
{
Task t = Task.Run(async () => await GetV1Async(ip));
tasks.Add(t);
}
if (Params.V2)
{
Task t = Task.Run(async () => await GetV2Async(ip));
tasks.Add(t);
}
if (Params.V3)
{
Task t = Task.Run(async () => await GetV3Async(ip));
tasks.Add(t);
}
}
}
await Task.WhenAll(tasks);
Код тормозит, для одного IP с двумя протоколами он выполняется ~25 секунд, в процессе вызывая жуткие тормоза и зависания окна. Но когда я использую профайлер асинхронного кода в VS2022 (Alt+F2
-> .NET Async
), то с ним мой код выполняется примерно в 10 раз меньше (~2.5 сек), то есть самой ситуации зависания не происходит в принципе, и ничего, тем более, увидеть в профайлере я не смогу.
Сам проект на NET 6, Avalonia 11.0.7
Ответы (1 шт):
Асинхронные методы не следует запихивать принудительно в поток.
Если асинхронный метод реализован правильно, то вот это
Task t = Task.Run(async () => await GetV1Async(ip));
tasks.Add(t);
Можно и нужно записать вот так
tasks.Add(GetV1Async(ip));
Это предотвратит совершенно ненужную в данном случае атаку на пул потоков. Как итог и подвисаний быть не должно.
Также можете почитать вот это как пример всяких ограничений при запуске большого количества асинхронных задач: Массовые асинхронные вызовы с ограничением на количество параллельных без семафора