Блокування виконавця
Більшість асинхронних середовищ виконання дозволяють лише одночасний запуск завдань вводу/виводу. Це означає, що завдання, що блокують процесор, блокуватимуть виконавця та запобігатимуть виконанню інших завдань. Простим обхідним шляхом є використання еквівалентних асинхронних методів, де це можливо.
use futures::future::join_all; use std::time::Instant; async fn sleep_ms(start: &Instant, id: u64, duration_ms: u64) { std::thread::sleep(std::time::Duration::from_millis(duration_ms)); println!( "ф'ючерс {id} спав протягом {duration_ms}ms, закінчив після {}ms", start.elapsed().as_millis() ); } #[tokio::main(flavor = "current_thread")] async fn main() { let start = Instant::now(); let sleep_futures = (1..=10).map(|t| sleep_ms(&start, t, t * 10)); join_all(sleep_futures).await; }
- 
Запустіть код і подивіться, що засинання відбуваються послідовно, а не одночасно.
 - 
Варіант
"current_thread"поміщає всі завдання в один потік. Це робить ефект більш очевидним, але помилка все ще присутня в багатопоточному варіанті. - 
Переключіть
std::thread::sleepнаtokio::time::sleepі дочекайтеся результату. - 
Іншим виправленням було б
tokio::task::spawn_blocking, який породжує фактичний потік і перетворює його дескриптор у ф’ючерс, не блокуючи виконавця. - 
Ви не повинні думати про завдання як про потоки ОС. Вони не відображаються 1 до 1, і більшість виконавців дозволять виконувати багато завдань в одному потоці ОС. Це особливо проблематично під час взаємодії з іншими бібліотеками через FFI, де ця бібліотека може залежати від локального сховища потоку або зіставлятися з певними потоками ОС (наприклад, CUDA). У таких ситуаціях віддайте перевагу
tokio::task::spawn_blocking. - 
Обережно використовуйте м’ютекси синхронізації. Утримування м’ютексу над
.awaitможе призвести до блокування іншого завдання, яке може виконуватися в тому самому потоці.