С# | Работа с Task

Подскажите, пожалуйста, кто знает, как правильно обращаться с Task'ами. Имею консольный проект. В нем расположен класс Program с методом Main(). Так же имею два других класса с методами Start(). Отсюда вопрос, как правильно реализовать асинхронное выполнение каждого из них? Нужно ли методам задавать возвращаемый тип Task или можно внутри методов создать задачи var task = new Task(async() => {...}) и после просто запустить task.Start(). Как вообще это правильно делается? И можно ли создавать List где будет храниться список всех запущенных задач и как-то потом контролироваться их выполнение, к примеру ожидание, пока не выполнятся некоторые из них? Пример моего скорее всего неграмотного кода:

class Program
{
    static void Main(string[] args)
    {
        Client.Main();
    }
}

internal static class Client
{
    internal static List<Task> tasks { get; set; }

    internal static async Task Main()
    {            
        Bot.Start();
        Html.Start();
    }        
}

internal static class Bot
{
    internal static async Task Start()
    {
        while(true)
        {
            ...
            await Task.Delay(10);
        }
    }
}

internal static class Html
{
    internal static List<string> list { get; set; }

    internal static async Task Start()
    {
        while(true)
        {
            ...
            await Task.Delay(10);
        }
    }
}

Вкратце о задумке. Есть телеграмм бот и какой-то парсящийся сайт. Эти задачи должны параллельно друг другу работать. Парсер должен постоянно обновлять свои данные, а бот должен забирать от парсера данные Html.list и пересылать их в группу, а так же принимать команды от пользователя и реагировать на них, например чтоб остановить парсер или запустить сканирование по другим параметрам. Вот я и думаю, как это грамотно можно сделать, а то у меня как-то не так все радужно выходит.


Ответы (1 шт):

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

Тема не простая, в двух словах не объяснить. Есть документация, лучше начать с нее.

Вот маленький пример.

static async Task Main(string[] args)
{
    Task[] tasks = new Task[2];
    tasks[0] = Task.Run(() => Multiply(1, 500));
    tasks[1] = GetNumberAsync();
    int[] results = await Task.WhenAll(tasks);
    Console.WriteLine($"Результаты: {results[0]}, {results[1]}");

    Console.ReadKey();
}

static int Multiply(int number, int times)
{
    int result = 0;
    for (int i = 0; i < times; i++)
    {
        Thread.Sleep(2);
        result += number;
    }
    Console.WriteLine("CPU вычисления завершены");
    return result;
}

static async Task<int> GetNumberAsync()
{
    await Task.Delay(1000);
    Console.WriteLine("Асинхронная операция завершена");
    return 42;
}

Смысл примера не только в том, что можно делать 100 дел одновременно, а в том что дела бывают разные. Бывают операции, требующие большой работы процессора - Task.Run именно для них. Бывают операции, требующие ответа из сети, от базы данных и т.д. Для таких операций запуск отдельного потока с помощью Task.Run не требуется. Их следует обязательно различать, потому что подходы к асинхронной реализации таких операций разные.

Да, здесь асинхронный Main, потому что мне требуется неблокирующее ожидание асинхронной операции Task.WhenAll, которая в свою очередь одновременно ожидает оба таска и завершается, когда оба завершены возвращая все результаты сразу.

Более подробно можно почитать здесь: Асинхронное программирование.

Еще материалы


Не вникая в подробности, исправляю показанный код

class Program
{
    static async Task Main(string[] args)
    {
        await Client.Run();
    }
}

internal static class Client
{
    internal static List<Task> tasks { get; set; }

    internal static async Task Run()
    {            
        await Task.WhenAll(Bot.Start(), Html.Start());
    }        
}

internal static class Bot
{
    internal static async Task Start()
    {
        while(true)
        {
            ...
            await Task.Delay(10);
        }
    }
}

internal static class Html
{
    internal static List<string> list { get; set; }

    internal static async Task Start()
    {
        while(true)
        {
            ...
            await Task.Delay(10);
        }
    }
}

Вот так будет работать, но я бы на вашем месте избавился бы от статиков.

→ Ссылка