🪄 Разрушаем магию: Твоя Future - это просто
Многие приходят в Rust из Go или Erlang и думают, что
Когда вы пишете
Смотрим код:
Под капотом LLVM генерирует невидимый
Когда рантайм вызывает метод
1. Выполняется кусок кода до первого
2. Состояние переключается (в
3. Управление возвращается обратно в рантайм! (Никакого блокирования потока).
Почему это круто? Такой
#rust #async #under_the_hood #future
👉 @rust_lib
enumМногие приходят в Rust из Go или Erlang и думают, что
async запускает легковесный поток (горутину). Забудьте. Асинхронный Rust работает совершенно иначе - он не аллоцирует стек под каждую задачу.Когда вы пишете
async fn, компилятор превращает вашу функцию в конечный автомат (State Machine).Смотрим код:
async fn fetch_data() {
let req = build_request();
let resp = send(req).await; // <-- Точка остановки 1
process(resp).await; // <-- Точка остановки 2
}
Под капотом LLVM генерирует невидимый
enum, который хранит локальные переменные между вызовами .await:
// Примерно так это видит компилятор:
enum FetchDataStateMachine {
Start,
WaitSend { req: Request }, // Храним локальные переменные
WaitProcess { resp: Response },
Done,
}
Когда рантайм вызывает метод
poll() у этой Future:1. Выполняется кусок кода до первого
.await.2. Состояние переключается (в
WaitSend).3. Управление возвращается обратно в рантайм! (Никакого блокирования потока).
Почему это круто? Такой
enum весит считанные байты. Вы можете создать миллион таких Future на обычном ноутбуке, и они займут всего пару десятков мегабайт оперативки. Никакая ОС не позволит вам создать миллион настоящих потоков.#rust #async #under_the_hood #future
👉 @rust_lib
👍20✍7❤3🥰1🤗1
⏰ Хватит дергать:
Окей, Future вернула управление (сказала
Здесь на сцену выходят Executor, Reactor и Waker.
• Executor - это менеджер задач. Он берет Future и вызывает у нее
• Reactor - это слушатель ОС (обертка над
• Waker - это тот самый пейджер, который выдают в бургерной.
Как это работает:
1. Future делает сетевой запрос и понимает, что данных еще нет.
2. Она регистрирует этот запрос в Reactor и отдает ему свой Waker (колбэк, в котором зашит ID этой Future).
3. Future возвращает
4. ...проходит время... Сетевая карта получает пакеты. ОС дергает
5. Reactor видит: "Ага, пришли данные для сокета X". Он берет Waker, привязанный к этому сокету, и вызывает
6. Waker сигнализирует Executor'у: "Задача #42 готова, помести её обратно в очередь на выполнение".
7. Executor снова вызывает
Никакого холостого хода CPU. Только чистая событийная архитектура.
#rust #async #waker #tokio #epoll
👉 @rust_lib
Waker и паттерн пейджераОкей, Future вернула управление (сказала
Poll::Pending). Но как рантайм узнает, что данные по сети пришли и пора снова вызвать poll()? Если он будет постоянно опрашивать все Future в цикле (while loop), ваш CPU улетит в космос на 100% загрузке.Здесь на сцену выходят Executor, Reactor и Waker.
• Executor - это менеджер задач. Он берет Future и вызывает у нее
poll().• Reactor - это слушатель ОС (обертка над
epoll в Linux, kqueue в macOS или IOCP в Windows). Он ждет событий от сетевой карты или диска.• Waker - это тот самый пейджер, который выдают в бургерной.
Как это работает:
1. Future делает сетевой запрос и понимает, что данных еще нет.
2. Она регистрирует этот запрос в Reactor и отдает ему свой Waker (колбэк, в котором зашит ID этой Future).
3. Future возвращает
Pending. Executor забывает про неё и идет выполнять другие задачи.4. ...проходит время... Сетевая карта получает пакеты. ОС дергает
epoll.5. Reactor видит: "Ага, пришли данные для сокета X". Он берет Waker, привязанный к этому сокету, и вызывает
waker.wake().6. Waker сигнализирует Executor'у: "Задача #42 готова, помести её обратно в очередь на выполнение".
7. Executor снова вызывает
poll(), и стейт-машина делает следующий шаг.Никакого холостого хода CPU. Только чистая событийная архитектура.
#rust #async #waker #tokio #epoll
👉 @rust_lib
👍12🔥4❤2🥰2👨💻1🤗1