Run: cargo run --example 01_basic_runtime
Learning goals
•
Understand what a runtime does for async Rust.
•
Know the difference between multi-thread and current-thread flavors.
•
See how async fn main becomes a normal fn main that drives a future to completion.
The program (conceptual walkthrough)
//! Minimal Tokio runtime entry point with `#[tokio::main]`.
// 1. Configure the Tokio runtime to use multiple threads,
// explicitly setting the background pool to exactly 2 worker threads.
#[tokio::main(flavor = "multi_thread", worker_threads = 2)]
// 2. Define the main entry point as an asynchronous function,
// allowing the use of the `.await` keyword inside its body.
async fn main() {
// 3. Print the startup message to the console to signal that
// the multi-threaded runtime has successfully initialized.
println!("Tokio multi-thread runtime started");
// 4. Pause execution for 100 milliseconds.
// Unlike standard thread::sleep, this non-blocking sleep yields control
// back to the Tokio scheduler, allowing worker threads to process
// other tasks while waiting.
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
// 5. Print the completion message once the 100ms timer expires
// and the execution resumes.
println!("Done");
} // 6. The main function scope ends, safely shutting down the runtime and exiting.
Rust
복사
What #[tokio::main] expands to (simplified)
Roughly, the macro generates:
1.
A normal fn main().
2.
A Runtime::new() (with options you pass to the macro).
3.
runtime.block_on(async { ... your main body ... }).
So you do not get magic threads inside main itself — you get a runtime that polls async tasks until they complete or park waiting for I/O/time.
async fn main
The body of main is a future. Every .await is a point where the runtime may:
•
Run other tasks on worker threads, or
•
Park this task until a timer/I/O event wakes it.
Here, sleep(100ms).await yields the task; the runtime schedules other work (none in this tiny program), then resumes after the timer fires.
Macro options used in this example
Option | Meaning |
flavor = "multi_thread" | Thread pool of worker threads runs tasks (good default for servers). |
worker_threads = 2 | Exactly two workers (default is num_cpus). |
Alternative: #[tokio::main(flavor = "current_thread")] — all tasks run on the thread that calls block_on. Useful for tests or single-threaded embedded-style loops.
Mental model: executor + reactor
Tokio combines:
•
Executor — runs task futures when they are ready.
•
Reactor — OS event notification (epoll/kqueue/IOCP) for sockets, and a timer wheel for sleep / interval.
When you .await on a sleep, the task is not blocking an OS thread; it is removed from the runnable queue until the timer fires.
Common mistakes
Mistake | Why it hurts |
Calling std::thread::sleep in async code | Blocks a worker thread; stalls other tasks. |
Creating many Runtime::new() in a library | Prefer one runtime per process or use Handle::current(). |
Heavy CPU work in async fn without spawn_blocking | Starves the executor (see lesson 10). |
Exercises
1.
Change worker_threads to 1 and observe behavior (should still work for this program).
2.
Switch to current_thread flavor — does output change? Why or why not?
3.
Add a second sleep and a spawn that prints between them — preview of tomorrow
안녕하세요
•
관련 기술 문의와 R&D 공동 연구 사업 관련 문의는 “glory@keti.re.kr”로 연락 부탁드립니다.
Hello 
•
For technical and business inquiries, please contact me at “glory@keti.re.kr”
