С# | Работа с 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 шт):
Тема не простая, в двух словах не объяснить. Есть документация, лучше начать с нее.
Вот маленький пример.
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);
}
}
}
Вот так будет работать, но я бы на вашем месте избавился бы от статиков.