c# как ускорить добавление потоков используя async-await

Рейтинг: 0Ответов: 1Опубликовано: 13.08.2023
    public static async void Async_actions(int n)
    {
        await Task.Run(() =>
        {
            actions(n)
        });
    }

Если создавать много действий через async-await, первые 10 потоков создадутся мгновенно, а далее создание каждого потока будет занимать - 1 секунду. Если софт работает в 300 потоков - после запуска софта приходится ждать по 5 минут, пока все потоки запустятся. Как ускорить процесс добавления новых потоков?

Ответы

▲ 4Принят

Вы не до конца понимаете суть многопоточности. 300 потоков, каждый из которых активно грузит процессор - это подвешивание машины и неэффективное использование процессорного времени. Когда количество потоков многократно превышает количество ядер процессора, то он очень много времени начинает тратить на переключение контекста, чтобы выделить своё время как можно большему числу потоков за единицу времени. В итоге на полезную работу времени уходит значительно меньше, чем могло бы. Количество создаваемых потоков оптимально не должно превышать количество ядер, умноженное на 2 (Environment.ProcessorCount * 2).

Далее, вы не до конца понимаете асинхронность. Ведь метод Async_actions с помощью ключевого слова async создает машину состояний, у которой всего-лишь одно состояние, то есть на запуск потока через Task.Run вы тратите лишние ресурсы. При этом сам по себе метод является async void, то есть работает в режиме "запустил и забыл", а await в нём бесполезен. Чтобы был не бесполезен, надо чтобв после него была какая-то работа, либо await должен быть в методе не один.

Что же делать, когда у вас 300 задач, а потоков вы можете запустить - всего, скажем 16 на 8-ядерной машине? Выстроить задачи в очередь. И с этим поможет инструмент синхронизации под названием семафор SemaphoreSlim.

public static async void AsyncAction(int n, SemaphoreSlim semaphore)
{
    await Task.Run(() =>
    {
        action(n);
    });
    semaphore.Release();
}

А запуск всего этого будет выглядеть так

int maxJobs = Environment.ProcessorCount * 2;
using SemaphoreSlim semaphore = new(maxJobs);
for (int i = 0; i < 300; i++)
{
    await semaphore.WaitAsync();
    AsyncAction(i, semaphore);
}

Вот так будет работать максимально быстро, так количество одновременно работающих потоков не будет критически большим для процессора.

Но этот код я бы улучшил, избавившись от async void. Почитайте эти ответы:

Альтернативно можно использовать TPL.