Как запустить два метода с возвращающий значением Task асинхронно(/одновременно/параллельно)

У меня есть два метода возвращающие Task, которые должны работать параллельно, один метод инкрементирует i, другой, при нажатии i на клавиатуре, отображает текущие значение:

static Task Method(ref int i, CancellationToken cancellationToken)
{
    while (i < 2100000000)
    {
        if (cancellationToken.IsCancellationRequested)
        {
            break;
        }

        i++;
    }

    return Task.CompletedTask;
}

static Task Info(ref int i, CancellationToken cancellationToken)
{
    while (!cancellationToken.IsCancellationRequested)
    {
        if (Console.KeyAvailable)
        {
            ConsoleKeyInfo keyInfo = Console.ReadKey();

            if (keyInfo.Key == ConsoleKey.I)
            {
                Console.Clear();
                Console.WriteLine(i);
            }

            if (keyInfo.Key == ConsoleKey.Q)
            {
                break;
            }
        }
    }

    return Task.CompletedTask;
}

я пытался их запустить в Main, но они запускаются синхронно:

static async Task Main()
{
    int i = 0;
    var cts = new CancellationTokenSource();

    Task task1 = Method(ref i, cts.Token);
    Task task2 = Info(ref i, cts.Token);

    await task1;
    cts.Cancel();

    Console.WriteLine("Готово");
    Console.ReadKey();
}

Подскажите, пожалуйста, что я не так сделал, и как же все-таки запустить данные методы параллельно?


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

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

Ваш код не работает потому, что

Task task1 = Method(ref i, cts.Token);

запускает Method и ждёт окончания его работы для того, чтобы присвоить результат его выполнения как задачи переменной слева.

Нужно сделать так:

var task1 = new Task(() => Method(ref i, cts.Token));
var task2 = new Task(() => Info(ref i, cts.Token));
task1.Start(); Console.WriteLine("task1");
task2.Start(); Console.WriteLine("task2");
Task.Delay(5000).Wait(); // это чтобы через 5 с полюбому закончилось
cts.Cancel();
Task.WaitAll(new Task[] { task1, task2 });

Можно убрать CancellationToken и заменить последние три строки на:

Task.WaitAny(new Task[] { task1, task2 }, 5000);

Можно и с таксраном (ой, опечатка, наверное):

var task1 = Task.Run(() => Method(ref i, cts.Token)); Console.WriteLine("task1");
var task2 = Task.Run(() => Info(ref i, cts.Token)); Console.WriteLine("task2");
Task.WaitAny(new Task[] { task1, task2 }, 5000);
→ Ссылка