Как многопоточность, так и асинхронное программирование являются разновидностями параллельного программирования. В многопоточности минимальная часть (которая является последовательной и не содержит параллельных частей) является потоком.
В асинхронном программировании минимальная часть - это асинхронный вызов процедуры (ACP).
Каждый поток имеет свой собственный стек вызовов, который потребляет значительный объем памяти (0,5 ... 1,0 МБ). В результате практический предел составляет около 10000 потоков на процесс.
У ACP нет собственного стека вызовов, он выполняется в потоке, заимствованном из некоторого пула потоков. Когда ACP ожидает входные данные, он не использует какой-либо поток и поэтому потребляет относительно небольшой объем памяти, и мы можем иметь миллионы ACP в одном процессе.
Но асинхронное программирование сложнее, чем многопоточное, и имеет больше ловушек.
«выполнение более одной задачи одновременно в одном потоке» - нет. Поток пользователя может порождать множество ACP, и они будут выполняться параллельно, но в рабочих потоках некоторого пула потоков (который может включать в себя исходный поток). Каждый рабочий поток выполняет асинхронные задачи последовательно, одна за другой.