В чем выгода асинхронности, если не во времени исполнения?

Не могу понять, в чем профит асинхронности в .net (не ui приложения).

Console.WriteLine("Start Main");
await PrintAsync();   
Console.WriteLine("End Main");

void Print()
{
    Thread.Sleep(3000);    
    Console.WriteLine("Print");
}

async Task PrintAsync()
{
    Console.WriteLine("Start PrintAsync"); 
    await Task.Run(() => Print());             
    Console.WriteLine("End PrintAsync"); 
}

Я бы понял, если бы вывод был таким:

Start Main
Start PrintAsync
End Main
Print
End PrintAsync

Т.е. профит такой: main работает, дошел до долгой задачи, отдал ее другому потоку, а сам продолжил работать, после чего подхватил ответ от той долгой задачи. Поток не останавливает свою работу. Но вывод, конечно, другой:

Start Main
Start PrintAsync
Print
End PrintAsync
End Main

Т.е. поток main все равно останавливается и ждет, пока придет ответ от асинхронной операции, отданной на сторону. Не проще ли тогда забить на асинхронность и делать все синхронно, раз все равно придется ждать?!


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

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

Полностью асинхронный код:

Console.WriteLine("Start Main");
// Запускаем задачу PrintAsync и спокойно идём дальше
var printTask = PrintAsync();
Console.WriteLine("End Main");
// Ждём завершения задачи PrintAsync
await printTask;

async Task PrintDelayAsync()
{
    // Ждём 3 секунды, но при этом поток не будет занят
    await Task.Delay(3000);
    Console.WriteLine("Print");
}

async Task PrintAsync()
{
    Console.WriteLine("Start PrintAsync");
    // Ждём пока метод отработает, но поток не занят
    await PrintDelayAsync();
    Console.WriteLine("End PrintAsync");
}

Весь этот код может выполняться даже одним потоком, при этом в моменты ожидания await этот поток освобождается может выполнять какие-то другие асинхронные задачи из вашего кода, если они у вас будут.

Пример параллельного исполнения задач:

Console.WriteLine("Start Main");
await PrintAsync();
Console.WriteLine("End Main");

async Task Print1Async()
{
    await Task.Delay(3000);
    Console.WriteLine("Print 1");
}

async Task Print2Async()
{
    await Task.Delay(2000);
    Console.WriteLine("Print 2");
}

async Task Print3Async()
{
    await Task.Delay(1000);
    Console.WriteLine("Print 3");
}

async Task PrintAsync()
{
    Console.WriteLine("Start PrintAsync");
    var tasks = new List<Task>() { Print1Async(), Print2Async(), Print3Async() };
    await Task.WhenAll(tasks);
    Console.WriteLine("End PrintAsync");
}

Вывод:

Start Main
Start PrintAsync
Print 3
Print 2
Print 1
End PrintAsync
End Main

То есть суть в чём - запустились сразу 3 задачи, выполнились они не в том порядке, как были запущены, а где меньше задержка, та раньше и выполнилась. При этом, повторюсь, достаточно одного потока, чтобы выполнять этот код. Что очень существенно, когда речь идёт о сильно загруженных программах типа веб-сервера или какого-то обработчика данных реального времени.

→ Ссылка
Автор решения: Leo

Асинхронность позволяет вынести отдельные задачи из основного потока в специальные асинхронные методы, и при этом более экономно использовать потоки.

Асинхронные методы выполняются в отдельных потоках. Однако при выполнении продолжительной операции поток асинхронного метода возвратится в пул потоков, и будет использоваться для других задач.

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

https://metanit.com/sharp/tutorial/13.3.php

→ Ссылка