Функция spawn_ blocking
pub fn spawn_blocking<F, R>(f: F) -> JoinHandle<R> ⓘ
rt.Запускает переданное замыкание в потоке, где допустимы блокирующие операции.
В общем случае выполнение блокирующего вызова или большого объёма вычислений в future без передачи управления проблематично, поскольку это может помешать исполнителю продвигать выполнение других future. Эта функция запускает переданное замыкание в потоке, предназначенном для блокирующих операций. Дополнительные сведения см. в разделе Задачи, ограниченные вычислительными ресурсами, и блокирующий код.
Tokio будет создавать дополнительные блокирующие потоки по мере их запроса через эту функцию, пока не будет достигнут верхний предел, заданный для Builder. После достижения верхнего предела задачи помещаются в очередь. По умолчанию предел числа потоков очень велик, поскольку spawn_blocking часто используется для различных типов операций ввода-вывода, которые нельзя выполнять асинхронно. При запуске кода с интенсивными вычислениями с помощью spawn_blocking следует учитывать этот высокий верхний предел. При выполнении множества вычислений с интенсивными вычислениями следует использовать семафор или другой примитив синхронизации, чтобы ограничить число вычислений, выполняемых параллельно. Специализированные исполнители задач с интенсивными вычислениями, например rayon, также могут подойти.
Эта функция предназначена для неасинхронных операций, которые в конечном итоге завершаются самостоятельно. Если нужно создать обычный поток, следует вместо неё использовать thread::spawn.
Обратите внимание, что задачи, запущенные с помощью spawn_blocking, нельзя отменить, поскольку они не являются асинхронными. Если вызвать abort для задачи spawn_blocking, это не окажет никакого эффекта, и задача продолжит выполняться как обычно. Исключение — если задача ещё не начала выполняться; в этом случае вызов abort может предотвратить её запуск.
При завершении работы исполнителя он попытается abort все задачи, включая задачи spawn_blocking. Однако задачи spawn_blocking нельзя отменить после начала выполнения, а значит, при завершении работы среды выполнения придётся бесконечно ждать, пока все запущенные задачи spawn_blocking не завершатся. С помощью shutdown_timeout можно прекратить ожидание по истечении заданного времени ожидания. Учтите, что это всё равно не отменит задачи — им просто будет разрешено продолжать выполняться после возврата метода. Блокирующую задачу можно отменить, если она ещё не начала выполняться, но это не гарантируется.
Когда использовать spawn_blocking, а когда — выделенные потоки
spawn_blocking предназначена для ограниченной по времени блокирующей работы, которая в конечном итоге завершается. Каждый вызов на время выполнения задачи занимает поток из пула блокирующих потоков среды выполнения. Поэтому долгоживущие задачи снижают эффективную ёмкость пула, что может задерживать другие блокирующие операции, когда пул заполнен и работа поставлена в очередь.
Для задач, выполняющихся бесконечно или в течение длительного времени (например, фоновых рабочих процессов или постоянных циклов обработки), предпочтительнее использовать выделенный поток, созданный с помощью thread::spawn.
Общее правило:
- Используйте
spawn_blockingдля кратковременных блокирующих операций - Используйте выделенные потоки для долгоживущих или постоянно выполняющихся блокирующих задач
Обратите внимание: если используется однопоточная среда выполнения, эта функция всё равно создаст дополнительные потоки для блокирующих операций. Единственный поток планировщика current-thread используется только для асинхронного кода.
Связанные API и шаблоны для взаимодействия асинхронного и блокирующего кода
В простых случаях достаточно, чтобы замыкание принимало входные параметры при создании и возвращало одно значение (или структуру, кортеж и т. д.).
В более сложных ситуациях, когда требуется передавать данные в синхронный контекст или из него потоком, модуль mpsc channel предоставляет методы blocking_send и blocking_recv для использования в неасинхронном коде, например в потоке, созданном с помощью spawn_blocking.
Ещё один вариант — SyncIoBridge для случаев, когда синхронный контекст работает с потоками байтов. Например, можно использовать асинхронный HTTP-клиент, такой как hyper, для получения данных, а затем выполнять сложный разбор тела ответа с помощью библиотеки, предназначенной для синхронного ввода-вывода.
Наконец, см. также раздел Взаимодействие с синхронным кодом, где обсуждается обратный случай — использование Tokio как части более крупной синхронной кодовой базы.
Примеры
Передача входного значения и получение результата вычислений:
use tokio::task;
// Initial input
let mut v = "Hello, ".to_string();
let res = task::spawn_blocking(move || {
// Stand-in for compute-heavy work or using synchronous APIs
v.push_str("world");
// Pass ownership of the value back to the asynchronous context
v
}).await?;
// `res` is the value returned from the thread
assert_eq!(res.as_str(), "Hello, world");Использование канала:
use tokio::task;
use tokio::sync::mpsc;
let (tx, mut rx) = mpsc::channel(2);
let start = 5;
let worker = task::spawn_blocking(move || {
for x in 0..10 {
// Stand in for complex computation
tx.blocking_send(start + x).unwrap();
}
});
let mut acc = 0;
while let Some(v) = rx.recv().await {
acc += v;
}
assert_eq!(acc, 95);
worker.await.unwrap();
MIT License
Copyright © Tokio Contributors
https://docs.rs/tokio/1.53.1/tokio/task/fn.spawn_blocking.html