Странное поведение профилировщика асинхронного кода

У меня есть код, который сканирует сеть по выбранным 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 шт):

Автор решения: aepot

Асинхронные методы не следует запихивать принудительно в поток.

Если асинхронный метод реализован правильно, то вот это

Task t = Task.Run(async () => await GetV1Async(ip));
tasks.Add(t);

Можно и нужно записать вот так

tasks.Add(GetV1Async(ip));

Это предотвратит совершенно ненужную в данном случае атаку на пул потоков. Как итог и подвисаний быть не должно.

Также можете почитать вот это как пример всяких ограничений при запуске большого количества асинхронных задач: Массовые асинхронные вызовы с ограничением на количество параллельных без семафора

→ Ссылка