Spec-Zone.ru › Tokio

Крейт 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 поддерживаются следующие функции:

  • sync
  • macros
  • io-util
  • rt
  • time

Включение любой другой функции (в том числе full) приведёт к ошибке компиляции.

Модуль time работает только на платформах WASM с поддержкой таймеров (например, wasm32-wasi). При использовании функций времени на платформе WASM без поддержки таймеров возникнет паника.

Также обратите внимание: если среда выполнения станет простаивать неопределённо долго, она немедленно вызовет панику вместо того, чтобы заблокироваться навсегда. На платформах без поддержки времени это означает, что среда выполнения ни при каких обстоятельствах не может простаивать.

Нестабильная поддержка WASM

Tokio также нестабильно поддерживает некоторые дополнительные функции WASM. Для этого требуется флаг tokio_unstable.

Этот флаг позволяет использовать tokio::net для целевой платформы wasm32-wasi. Однако для сетевых типов доступны не все методы, поскольку WASI в настоящее время не поддерживает создание новых сокетов из WASM. Поэтому сейчас сокеты необходимо создавать с помощью трейта FromRawFd.

Повторные экспорты

pub use task::spawn;rt

Модули

docdocsrs и Unix
Типы, документация которых размещена непосредственно в крейте Tokio, но которые фактически находятся не здесь.
fsfs
Асинхронные средства для работы с файлами.
io
Трейты, вспомогательные средства и определения типов для асинхронного ввода-вывода.
net
Привязки TCP/UDP/Unix для tokio.
processprocess
Реализация асинхронного управления процессами для Tokio.
runtimert
Среда выполнения Tokio.
signalsignal
Асинхронная обработка сигналов в Tokio.
stream
Поскольку трейт Stream вошёл в std позже выпуска Tokio 1.0, большинство потоковых средств Tokio перенесено в крейт tokio-stream.
syncsync
Примитивы синхронизации для использования в асинхронных контекстах.
taskrt
Асинхронные зелёные потоки.
timetime
Средства для отслеживания времени.

Макросы

joinmacros
Ожидает завершения нескольких параллельных ветвей и возвращает результат, когда завершаются все ветви.
pin
Закрепляет значение в стеке.
selectmacros
Ожидает завершения нескольких параллельных ветвей и возвращает результат, когда завершается первая ветвь, отменяя остальные.
task_localrt
Объявляет новый ключ, локальный для задачи, типа tokio::task::LocalKey.
try_joinmacros
Ожидает завершения нескольких параллельных ветвей и возвращает результат, когда завершаются все ветви с Ok(_) или при первой Err(_).

Макросы-атрибуты

mainmacros и rt
Помечает асинхронную функцию для выполнения выбранной средой выполнения. Этот макрос помогает настроить Runtime, избавляя пользователя от необходимости напрямую использовать Runtime или Builder.
testmacros и rt
Помечает асинхронную функцию для выполнения средой выполнения и подходит для тестовой среды. Этот макрос помогает настроить Runtime, избавляя пользователя от необходимости напрямую использовать Runtime или Builder.

MIT License
Copyright © Tokio Contributors
https://docs.rs/tokio/1.53.1/tokio/index.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API