Используя шаблон async / await, вы намереваетесь, чтобы ваш метод Calc
запускался как задача:
Task<int> a = Calc(18000);
Нам нужно sh установить, что задачи обычно асинхронные по своей природе, но не parallel - параллелизм - это особенность потоков. Однако под капотом некоторый поток будет использоваться для выполнения ваших задач. В зависимости от контекста, в котором вы запускаете свой код, несколько задач могут или не могут выполняться параллельно или последовательно, но они будут (разрешены) асинхронными.
Хороший способ усвоить это - представить учителя (поток) отвечая на вопрос (задачи) в классе. Учитель никогда не сможет ответить на два разных вопроса одновременно - т. Е. Параллельно - но он сможет ответить на вопросы нескольких учеников, а также может быть прерван новыми вопросами между ними.
В частности, async / await - это функция совместной многопроцессорной обработки (акцент на «кооперативности»), при которой задачи могут быть запланированы в потоке, только если этот поток свободен - и если какая-то задача уже выполняется в этом потоке, она должна вручную уступить дорогу. (Наличие и количество потоков, доступных для выполнения, в свою очередь, зависит от среды, в которой выполняется ваш код.)
При запуске Task.Delay(1)
код сообщает, что он намеревается засыпать, что сигнализирует планировщик , который может выполнить другая задача, тем самым обеспечивая асинхронность. То, как он используется в вашем коде, по сути, является немного худшей версией Task.Yield
(вы можете найти более подробную информацию об этом в комментариях ниже).
В общем, всякий раз, когда вы await
, метод, который в данный момент выполняется, "возвращается", помечая код после него как продолжение. Это продолжение будет выполнено только тогда, когда планировщик задач снова выберет текущую задачу для выполнения. Это может произойти, если никакая другая задача в настоящее время не выполняется и не ожидает выполнения - например, потому что все они дали результат или await
что-то «длительное».
В вашем примере метод Calc
дает результат из-за Task.Delay
и выполнение возвращается к методу Main
. Это, в свою очередь, входит в следующий метод Calc
, и шаблон повторяется. Как было установлено в других ответах, эти продолжения могут выполняться или не выполняться в разных потоках, в зависимости от среды - без контекста синхронизации (например, в консольном приложении) это может произойти . Для ясности: это не функция Task.Delay
или async / await, а конфигурация, в которой выполняется ваш код. Если вам требуется параллелизм , используйте соответствующие потоки или убедитесь, что ваши задачи запускаются таким образом, что они поощряют использование нескольких потоков.
В другом примечании: всякий раз, когда вы намереваетесь запускать синхронный код в асинхронном режиме, используйте Task.Run()
для его выполнения. Это гарантирует, что он не будет слишком мешать вам, всегда используя фоновый поток. Этот SO-ответ на LongRunning
задачах может быть поучительным.