Крейт tokio
Среда выполнения для создания надёжных сетевых приложений без ущерба для скорости.
Tokio — это управляемая событиями платформа неблокирующего ввода-вывода для создания асинхронных приложений на языке программирования Rust. На высоком уровне она предоставляет несколько основных компонентов:
- Инструменты для работы с асинхронными задачами, включая примитивы синхронизации и каналы, а также тайм-ауты, задержки и интервалы.
- API для выполнения асинхронного ввода-вывода, включая сокеты TCP и UDP, операции с файловой системой, а также управление процессами и сигналами.
- Среда выполнения для выполнения асинхронного кода, включающая планировщик задач, драйвер ввода-вывода на основе очереди событий операционной системы (
epoll,kqueue,IOCPи т. д.) и высокопроизводительный таймер.
Документацию для изучения можно найти на сайте.
Знакомство с Tokio
Tokio состоит из ряда модулей, предоставляющих разнообразные возможности, необходимые для реализации асинхронных приложений на Rust. В этом разделе мы кратко познакомимся с Tokio и рассмотрим основные API и способы их использования.
Проще всего начать работу, включив все функции. Для этого включите флаг функции full:
tokio = { version = "1", features = ["full"] }Создание приложений
Tokio отлично подходит для создания приложений, и большинству пользователей в этом случае не стоит слишком беспокоиться о том, какие функции выбрать. Если вы не уверены, мы рекомендуем использовать full, чтобы избежать препятствий при разработке приложения.
Пример
В этом примере показан самый быстрый способ начать работу с Tokio.
tokio = { version = "1", features = ["full"] }Создание библиотек
Как автор библиотеки, вы должны стремиться предоставить крейт с минимальным набором возможностей, основанный на Tokio. Для этого включайте только необходимые вам функции. Это позволит пользователям подключать ваш крейт, не включая ненужные функции.
Пример
В этом примере показано, как можно импортировать функции для библиотеки, которой нужно только tokio::spawn и использовать TcpStream.
tokio = { version = "1", features = ["rt", "net"] }Работа с задачами
Асинхронные программы на Rust строятся на основе лёгких неблокирующих единиц выполнения, называемых задачами. Модуль tokio::task предоставляет важные инструменты для работы с задачами:
- Функция
spawnи типJoinHandleпозволяют соответственно запланировать новую задачу в среде выполнения Tokio и дождаться результата запущенной задачи; - функции для выполнения блокирующих операций в контексте асинхронной задачи.
Модуль tokio::task доступен только при включённом флаге функции «rt».
Модуль tokio::sync содержит примитивы синхронизации для обмена данными и совместного доступа к ним. К ним относятся:
- каналы (
oneshot,mpsc,watchиbroadcast) для передачи значений между задачами; - неблокирующий
Mutexдля управления доступом к общему изменяемому значению; - асинхронный тип
Barrier, позволяющий нескольким задачам синхронизироваться перед началом вычислений.
Модуль tokio::sync доступен только при включённом флаге функции «sync».
Модуль tokio::time предоставляет средства для отслеживания времени и планирования работы. В их число входят функции для установки тайм-аутов для задач, отложенного выполнения работы или повторения операции через заданные интервалы.
Для использования tokio::time необходимо включить флаг функции «time».
Наконец, Tokio предоставляет среду выполнения для выполнения асинхронных задач. Большинство приложений могут использовать макрос #[tokio::main], чтобы запустить свой код в среде выполнения Tokio. Однако этот макрос предоставляет только базовые возможности настройки. В качестве альтернативы модуль tokio::runtime предлагает более мощные API для настройки и управления средами выполнения. Используйте этот модуль, если макрос #[tokio::main] не предоставляет нужных вам возможностей.
Для использования среды выполнения необходимо включить флаг функции «rt» или «rt-multi-thread», чтобы задействовать соответственно однопоточный планировщик с текущим потоком или многопоточный планировщик. Подробности см. в документации модуля runtime. Кроме того, флаг функции «macros» включает атрибуты #[tokio::main] и #[tokio::test].
Задачи с интенсивными вычислениями и блокирующий код
Tokio может одновременно выполнять множество задач на нескольких потоках, многократно переключая текущую задачу в каждом потоке. Однако такое переключение возможно только в точках .await, поэтому код, который долго не достигает .await, не позволит выполняться другим задачам. Для решения этой проблемы Tokio предоставляет два типа потоков: основные и блокирующие.
Основные потоки выполняют весь асинхронный код; по умолчанию Tokio создаёт по одному потоку на каждое ядро процессора. Переопределить значение по умолчанию можно с помощью переменной среды TOKIO_WORKER_THREADS.
Блокирующие потоки создаются по мере необходимости и могут использоваться для выполнения блокирующего кода, который в противном случае препятствовал бы выполнению других задач. Если такие потоки не используются в течение заданного времени, они остаются активными; это время можно настроить с помощью thread_keep_alive. Поскольку Tokio не может переключать блокирующие задачи так, как он переключает асинхронный код, верхний предел количества блокирующих потоков очень велик. Эти ограничения можно настроить с помощью Builder.
Чтобы запустить блокирующую задачу, используйте функцию spawn_blocking.
#[tokio::main]
async fn main() {
// This is running on a core thread.
let blocking_task = tokio::task::spawn_blocking(|| {
// This is running on a blocking thread.
// Blocking here is ok.
});
// We can wait for the blocking task like this:
// If the blocking task panics, the unwrap below will propagate the
// panic.
blocking_task.await.unwrap();
}Если ваш код интенсивно использует процессор и вы хотите ограничить число потоков, используемых для его выполнения, используйте отдельный пул потоков, предназначенный для задач с интенсивными вычислениями. Например, для таких задач можно использовать библиотеку rayon. Также можно создать дополнительную среду выполнения Tokio для задач с интенсивными вычислениями, но в этом случае убедитесь, что она выполняет только такие задачи: задачи с интенсивным вводом-выводом в этой среде будут работать плохо.
Совет: при использовании rayon можно передать результат обратно в Tokio по каналу oneshot после завершения задачи rayon.
Асинхронный ввод-вывод
Помимо планирования и выполнения задач, Tokio предоставляет всё необходимое для асинхронного выполнения операций ввода-вывода.
Модуль tokio::io предоставляет основные асинхронные примитивы ввода-вывода Tokio: трейты AsyncRead, AsyncWrite и AsyncBufRead. Кроме того, при включённом флаге функции «io-util» модуль предоставляет комбинаторы и функции для работы с этими трейтами, образуя асинхронный аналог std::io.
Tokio также включает API для выполнения различных операций ввода-вывода и асинхронного взаимодействия с операционной системой. К ним относятся:
-
tokio::net, содержащий неблокирующие версии TCP, UDP и сокетов домена Unix (включается флагом функции «net»); -
tokio::fs, аналогичныйstd::fs, но предназначенный для асинхронных операций ввода-вывода с файловой системой (включается флагом функции «fs»); -
tokio::signalдля асинхронной обработки сигналов ОС Unix и Windows (включается флагом функции «signal»); -
tokio::processдля запуска и управления дочерними процессами (включается флагом функции «process»).
Примеры
Простой TCP-сервер эхо:
use tokio::net::TcpListener;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let listener = TcpListener::bind("127.0.0.1:8080").await?;
loop {
let (mut socket, _) = listener.accept().await?;
tokio::spawn(async move {
let mut buf = [0; 1024];
// In a loop, read data from the socket and write the data back.
loop {
let n = match socket.read(&mut buf).await {
// socket closed
Ok(0) => return,
Ok(n) => n,
Err(e) => {
eprintln!("failed to read from socket; err = {:?}", e);
return;
}
};
// Write the data back
if let Err(e) = socket.write_all(&buf[0..n]).await {
eprintln!("failed to write to socket; err = {:?}", e);
return;
}
}
});
}
}Флаги функций
Tokio использует набор флагов функций, чтобы уменьшить объём компилируемого кода. Можно включить только отдельные функции. По умолчанию Tokio не включает ни одной функции, но позволяет включить подмножество функций для конкретного сценария использования. Ниже приведён список доступных флагов функций. Над каждой функцией, структурой и трейтом также указаны необходимые для их использования флаги функций. Если вы только начинаете работать с Tokio, рекомендуется использовать флаг функции full, который включает все общедоступные API. Однако учтите, что при этом будут подключены многие дополнительные зависимости, которые могут вам не понадобиться.
-
full: включает все перечисленные ниже функции, кромеtest-utilи нестабильных функций. -
rt: включаетtokio::spawn, планировщик с текущим потоком и вспомогательные средства, не связанные с планировщиком. -
rt-multi-thread: включает более ресурсоёмкий многопоточный планировщик с вытеснением задач из очередей. -
io-util: включает трейтыExt, основанные на вводе-выводе. -
io-std: включает типыStdout,StdinиStderr. -
net: включает типыtokio::net, такие какTcpStream,UnixStreamиUdpSocket, а также (в Unix-подобных системах)AsyncFdи (во FreeBSD)PollAio. -
time: включает типыtokio::timeи позволяет планировщикам включать встроенный таймер. -
process: включает типыtokio::process. -
macros: включает макросы#[tokio::main]и#[tokio::test]. -
sync: включает все типыtokio::sync. -
signal: включает все типыtokio::signal. -
fs: включает типыtokio::fs. -
test-util: включает инфраструктуру для тестирования среды выполнения Tokio. -
parking_lot: в качестве возможной оптимизации использует внутри крейта примитивы синхронизации крейтаparking_lot. Кроме того, эта зависимость необходима для создания некоторых наших примитивов в контекстеconst.MSRVможет увеличиваться в зависимости от используемого выпускаparking_lot.
Примечание: трейты AsyncRead и AsyncWrite не требуют включения каких-либо функций и доступны всегда.
Нестабильные функции
Некоторые флаги функций доступны только при указании флага tokio_unstable:
-
tracing: включает события трассировки. -
schedule-latency: позволяет измерять задержки при планировании задач. -
io-uring: включаетio-uring(только Linux). -
taskdump: включаетtaskdump(только Linux).
Этот флаг также открывает доступ к нестабильным API.
Этот флаг включает нестабильные функции. Публичный API этих функций может измениться в выпусках 1.x. Для включения этих функций аргумент --cfg tokio_unstable необходимо передать в rustc при компиляции. Это позволяет явно согласиться на использование функций, которые могут нарушать соглашения semver, поскольку Cargo пока не поддерживает напрямую такое явное согласие.
Указать этот аргумент можно в файле .cargo/config.toml вашего проекта:
[build]
rustflags = ["--cfg", "tokio_unstable"][build] не следует помещать в файл Cargo.toml. Вместо этого его необходимо добавить в файл конфигурации Cargo .cargo/config.toml. Также можно указать его с помощью переменной среды:
## Many *nix shells:
export RUSTFLAGS="--cfg tokio_unstable"
cargo build## Windows PowerShell:
$Env:RUSTFLAGS="--cfg tokio_unstable"
cargo buildПоддерживаемые платформы
В настоящее время Tokio гарантирует поддержку следующих платформ:
- Linux
- Windows
- Android (уровень API 21)
- macOS
- iOS
- FreeBSD
В дальнейшем Tokio продолжит поддерживать эти платформы. Однако в будущих выпусках могут измениться такие требования, как минимальная версия libc в Linux, уровень API в Android или поддерживаемый выпуск FreeBSD.
Помимо перечисленных выше платформ, Tokio должен работать на всех платформах, поддерживаемых крейтом mio. Более длинный список приведён в документации mio. Однако в будущем поддержка этих дополнительных платформ может быть прекращена.
Обратите внимание, что Wine считается отдельной платформой, отличной от Windows. Дополнительные сведения о поддержке Wine см. в документации mio.
Поддержка WASM
Tokio частично поддерживает платформу WASM. Без флага tokio_unstable поддерживаются следующие функции:
syncmacrosio-utilrttime
Включение любой другой функции (в том числе full) приведёт к ошибке компиляции.
Модуль time работает только на платформах WASM с поддержкой таймеров (например, wasm32-wasi). При использовании функций времени на платформе WASM без поддержки таймеров возникнет паника.
Также обратите внимание: если среда выполнения станет простаивать неопределённо долго, она немедленно вызовет панику вместо того, чтобы заблокироваться навсегда. На платформах без поддержки времени это означает, что среда выполнения ни при каких обстоятельствах не может простаивать.
Нестабильная поддержка WASM
Tokio также нестабильно поддерживает некоторые дополнительные функции WASM. Для этого требуется флаг tokio_unstable.
Этот флаг позволяет использовать tokio::net для целевой платформы wasm32-wasi. Однако для сетевых типов доступны не все методы, поскольку WASI в настоящее время не поддерживает создание новых сокетов из WASM. Поэтому сейчас сокеты необходимо создавать с помощью трейта FromRawFd.
Повторные экспорты
-
pub use task::spawn;rt
Модули
-
doc
docsrsи Unix - Типы, документация которых размещена непосредственно в крейте Tokio, но которые фактически находятся не здесь.
-
fs
fs - Асинхронные средства для работы с файлами.
- io
- Трейты, вспомогательные средства и определения типов для асинхронного ввода-вывода.
- net
- Привязки TCP/UDP/Unix для
tokio. -
process
process - Реализация асинхронного управления процессами для Tokio.
-
runtime
rt - Среда выполнения Tokio.
-
signal
signal - Асинхронная обработка сигналов в Tokio.
- stream
- Поскольку трейт
Streamвошёл вstdпозже выпуска Tokio 1.0, большинство потоковых средств Tokio перенесено в крейтtokio-stream. -
sync
sync - Примитивы синхронизации для использования в асинхронных контекстах.
-
task
rt - Асинхронные зелёные потоки.
-
time
time - Средства для отслеживания времени.
Макросы
-
join
macros - Ожидает завершения нескольких параллельных ветвей и возвращает результат, когда завершаются все ветви.
- pin
- Закрепляет значение в стеке.
-
select
macros - Ожидает завершения нескольких параллельных ветвей и возвращает результат, когда завершается первая ветвь, отменяя остальные.
-
task_
local rt - Объявляет новый ключ, локальный для задачи, типа
tokio::task::LocalKey. -
try_
join macros - Ожидает завершения нескольких параллельных ветвей и возвращает результат, когда завершаются все ветви с
Ok(_)или при первойErr(_).
Макросы-атрибуты
-
main
macrosиrt - Помечает асинхронную функцию для выполнения выбранной средой выполнения. Этот макрос помогает настроить
Runtime, избавляя пользователя от необходимости напрямую использовать Runtime или Builder. -
test
macrosиrt - Помечает асинхронную функцию для выполнения средой выполнения и подходит для тестовой среды. Этот макрос помогает настроить
Runtime, избавляя пользователя от необходимости напрямую использовать Runtime или Builder.
MIT License
Copyright © Tokio Contributors
https://docs.rs/tokio/1.53.1/tokio/index.html