Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Блокировка исполнителя

Большинство асинхронных рантаймов позволяют одновременно выполняться только задачам ввода-вывода. Это значит, что задачи, блокирующие ЦП, заблокируют исполнитель и не дадут выполняться другим задачам. Простой обходной путь — использовать асинхронные эквивалентные методы, где это возможно.

// Copyright 2024 Google LLC
// SPDX-License-Identifier: Apache-2.0

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!(
        "future {id} slept for {duration_ms}ms, finished after {}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;
}
This slide should take about 10 minutes.
  • Запустите код и убедитесь, что паузы выполняются последовательно, а не одновременно.

  • Вариант "current_thread" помещает все задачи в один поток. Это делает эффект более очевидным, но ошибка сохраняется и в многопоточном варианте.

  • Замените std::thread::sleep на tokio::time::sleep и дождитесь его результата через .await.

  • Другое решение — tokio::task::spawn_blocking, который создаёт настоящий поток и превращает его дескриптор во фьючерс, не блокируя исполнитель.

  • Не стоит считать задачи потоками ОС. Они не соответствуют друг другу 1 к 1, и исполнители позволяют нескольким задачам выполняться в одном потоке ОС. Это особенно проблемно при взаимодействии с другими библиотеками через FFI, где библиотека может зависеть от локального хранилища потока или сопоставлять себя с конкретными потоками ОС (например, CUDA). В таких ситуациях предпочитайте tokio::task::spawn_blocking.

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