AWAIT vs SPAWN
1. await - приостановка выполнения задачи
Что делает:
awaitприостанавливает выполнение текущей асинхронной функции до тех пор, пока будущее (Future) не будет завершено. При этом он не блокирует весь поток - пока одна задача ждет, другие могут выполняться.
Простой пример:
use tokio::time::{sleep, Duration}; async fn download_file(filename: &str) -> String { println!("Начинаю загрузку: {}", filename); sleep(Duration::from_secs(2)).await; // ⏸️ ПАУЗА здесь println!("Завершил загрузку: {}", filename); format!("Содержимое {}", filename) } #[tokio::main] async fn main() { println!("Начало программы"); let content = download_file("document.txt").await; println!("Получили: {}", content); println!("Конец программы"); }
Вывод:
Начало программы
Начинаю загрузку: document.txt
(ждем 2 секунды...)
Завершил загрузку: document.txt
Получили: Содержимое document.txt
Конец программы
2. tokio::spawn - запуск параллельной задачи
Что делает:
tokio::spawnсоздает новую асинхронную задачу, которая выполняется параллельно с текущей. ВозвращаетJoinHandle, через который можно дождаться результата.
Простой пример:
use tokio::time::{sleep, Duration}; async fn download_file(filename: &str) -> String { println!("Начинаю загрузку: {}", filename); sleep(Duration::from_secs(2)).await; println!("Завершил загрузку: {}", filename); format!("Содержимое {}", filename) } #[tokio::main] async fn main() { println!("Начало программы"); // Запускаем две загрузки параллельно let task1 = tokio::spawn(async { download_file("document1.txt").await }); let task2 = tokio::spawn(async { download_file("document2.txt").await }); // Ждем завершения обеих задач let result1 = task1.await.unwrap(); let result2 = task2.await.unwrap(); println!("Результаты: {} и {}", result1, result2); println!("Конец программы"); }
Вывод (обратите внимание на параллельность):
Начало программы
Начинаю загрузку: document1.txt
Начинаю загрузку: document2.txt
(ждем 2 секунды, но обе загрузки идут параллельно!)
Завершил загрузку: document1.txt
Завершил загрузку: document2.txt
Результаты: Содержимое document1.txt и Содержимое document2.txt
Конец программы
Ключевые различия
| Аспект | await | tokio::spawn |
|---|---|---|
| Параллелизм | Последовательное выполнение | Параллельное выполнение |
| Блокировка | Не блокирует поток, только текущую задачу | Создает новую независимую задачу |
| Использование | Для ожидания результата Future | Для запуска параллельной работы |
| Результат | Значение Future | JoinHandle для ожидания результата |
Схема работы await (последовательное выполнение):
Схема работы tokio::spawn (параллельное выполнение):
Комбинированный пример
use tokio::time::{sleep, Duration}; async fn expensive_calculation(n: u32) -> u32 { println!("Начинаю вычисление {}", n); sleep(Duration::from_secs(1)).await; println!("Завершил вычисление {}", n); n * n } #[tokio::main] async fn main() { // ПЛОХО: последовательное выполнение (3 секунды) let start = std::time::Instant::now(); let _a = expensive_calculation(1).await; let _b = expensive_calculation(2).await; let _c = expensive_calculation(3).await; println!("Последовательное выполнение: {:?}", start.elapsed()); // ХОРОШО: параллельное выполнение (~1 секунда) let start = std::time::Instant::now(); let task1 = tokio::spawn(expensive_calculation(1)); let task2 = tokio::spawn(expensive_calculation(2)); let task3 = tokio::spawn(expensive_calculation(3)); // await для каждого JoinHandle let _a = task1.await.unwrap(); let _b = task2.await.unwrap(); let _c = task3.await.unwrap(); println!("Параллельное выполнение: {:?}", start.elapsed()); }
Итог
await= "Подожди, пока эта операция завершится, прежде чем продолжать"tokio::spawn= "Запусти эту работу в фоне, я могу заниматься другими делами"
Используйте await для последовательных операций, которые зависят друг от друга, и tokio::spawn для независимых операций, которые можно выполнять параллельно.