A learning project exploring Tokio async runtime concepts in Rust through practical examples. Each binary demonstrates a specific async programming concept, building complexity sequentially.
Tokio is an asynchronous runtime for Rust that provides the building blocks for writing fast, reliable, and scalable network applications. It's built on top of Rust's async/await syntax and provides:
- Async runtime: Efficiently manages thousands of concurrent tasks
- Async I/O: Non-blocking file and network operations
- Concurrency primitives: Channels, mutexes, and other synchronization tools
- Timers: Async sleep and interval functionality
This project follows a sequential learning approach, with each binary building upon previous concepts:
- File:
src/bin/concurrent_tasks.rs - Concept: Spawning concurrent async tasks
- Key Learning: How to run multiple async functions concurrently without blocking
- Run:
cargo run --bin concurrent_tasks
- File:
src/bin/sleep.rs - Concept: Async sleep vs blocking sleep, background tasks
- Key Learning: Difference between
tokio::time::sleep()andstd::thread::sleep() - Run:
cargo run --bin sleep
- Files:
src/bin/read_file.rssrc/bin/write_file.rs
- Concept: Non-blocking file I/O operations
- Key Learning: Using
tokio::fsfor async file operations - Run:
cargo run --bin read_filecargo run --bin write_file
- File:
src/bin/shared_state.rs - Concept: Async-safe mutexes and shared state management
- Key Learning: Difference between
std::sync::Mutexandtokio::sync::Mutex - Run:
cargo run --bin shared_state
- File:
src/bin/channels.rs - Concept: Multi-producer, single-consumer channels for task communication
- Key Learning: Using
mpsc::channelfor async message passing between tasks - Run:
cargo run --bin channels
- File:
src/bin/timeout.rs - Concept: Setting time limits for async operations
- Key Learning: Using
tokio::time::timeoutto prevent operations from hanging indefinitely - Run:
cargo run --bin timeout
- File:
src/bin/ticker.rs - Concept: Running tasks at regular intervals
- Key Learning: Using
tokio::time::intervalfor periodic async task execution - Run:
cargo run --bin ticker
- File:
src/bin/delay.rs - Concept: Implementing custom futures with manual polling
- Key Learning: Understanding
Futuretrait,Poll, wakers, and how async/await works under the hood - Run:
cargo run --bin delay
- File:
src/bin/notify.rs - Concept: Signaling between tasks using one-shot notifications
- Key Learning: Using
tokio::sync::Notifyfor simple task coordination without data transfer - Run:
cargo run --bin notify
- File:
src/bin/select.rs - Concept: Racing multiple async operations and handling the first to complete
- Key Learning: Using
tokio::select!macro to wait on multiple futures simultaneously - Run:
cargo run --bin select
- File:
src/bin/one_shot_channel.rs - Concept: Single-use channels for one-time value transfer between tasks
- Key Learning: Using
tokio::sync::oneshotfor simple request-response patterns - Run:
cargo run --bin one_shot_channel
- File:
src/bin/stream.rs - Concept: Processing sequences of async values with streams
- Key Learning: Using
tokio_streamfor handling collections of async data - Run:
cargo run --bin stream
- File:
src/bin/bridge_async_in_sync.rs - Concept: Running async code from synchronous contexts
- Key Learning: Using
tokio::runtime::Builderto manually create and manage Tokio runtimes - Run:
cargo run --bin bridge_async_in_sync
- File:
src/bin/signal.rs - Concept: Handling system signals asynchronously
- Key Learning: Using
tokio::signalfor graceful shutdown and signal processing - Run:
cargo run --bin signal
- File:
src/bin/cancellation_token.rs - Concept: Cooperative cancellation of async tasks
- Key Learning: Using
tokio_util::sync::CancellationTokenfor graceful task termination - Run:
cargo run --bin cancellation_token
- File:
src/bin/task_tracker.rs - Concept: Tracking and waiting for multiple spawned tasks to complete
- Key Learning: Using
tokio_util::task::TaskTrackerfor managing task lifecycle and completion - Run:
cargo run --bin task_tracker
- File:
src/bin/test_async.rs - Concept: Writing unit tests for async functions
- Key Learning: Using
#[tokio::test]attribute to test async code with assertions - Run:
cargo test --bin test_async
- File:
src/bin/task_tracker_with_cancellation.rs - Concept: Combining task tracking with cooperative cancellation
- Key Learning: Using
TaskTrackerandCancellationTokentogether for managing cancellable task groups - Run:
cargo run --bin task_tracker_with_cancellation
- File:
src/bin/once_cell.rs - Concept: One-time async initialization of static values
- Key Learning: Using
tokio::sync::OnceCellfor lazy initialization that happens only once, even with concurrent access - Run:
cargo run --bin once_cell
src/
├── bin/ # Binary examples
│ ├── concurrent_tasks.rs # Basic async task spawning
│ ├── sleep.rs # Async timers and sleep
│ ├── read_file.rs # Async file reading
│ ├── write_file.rs # Async file writing
│ ├── shared_state.rs # Async mutexes and shared state
│ ├── channels.rs # Async channels for communication
│ ├── timeout.rs # Timeout handling for async operations
│ ├── ticker.rs # Periodic task execution with intervals
│ ├── delay.rs # Custom Future implementation with manual polling
│ ├── notify.rs # One-shot notifications between tasks
│ ├── select.rs # Racing async operations with select! macro
│ ├── one_shot_channel.rs # Single-use channels for one-time value transfer
│ ├── stream.rs # Processing sequences of async values with streams
│ ├── bridge_async_in_sync.rs # Running async code from synchronous contexts
│ ├── signal.rs # Handling system signals asynchronously
│ ├── cancellation_token.rs # Cooperative cancellation of async tasks
│ ├── task_tracker.rs # Tracking and waiting for multiple spawned tasks
│ ├── test_async.rs # Writing unit tests for async functions
│ ├── task_tracker_with_cancellation.rs # Combining task tracking with cancellation
│ └── once_cell.rs # Lazy async initialization of static values
├── lib.rs # Library entry point (exposes utils module)
├── main.rs # Main binary entry point
└── utils.rs # Shared utilities including tracing logger setup- Rust 1.80+ with Cargo
- Basic understanding of Rust syntax and async/await
# Clone and navigate to project
cd hello-tokio-async-rust
# Run any specific example
cargo run --bin concurrent_tasks
cargo run --bin sleep
cargo run --bin read_file
cargo run --bin write_file
cargo run --bin shared_state
cargo run --bin channels
cargo run --bin timeout
cargo run --bin ticker
cargo run --bin delay
cargo run --bin notify
cargo run --bin select
cargo run --bin one_shot_channel
cargo run --bin stream
cargo run --bin bridge_async_in_sync
cargo run --bin signal
cargo run --bin cancellation_token
cargo run --bin task_tracker
cargo run --bin task_tracker_with_cancellation
cargo run --bin once_cell
# Run async tests
cargo test --bin test_async
- Understanding
async fnand.await - The
#[tokio::main]macro - Task spawning with
tokio::spawn
- Running multiple async tasks concurrently
- Non-blocking operations
- Task handles and awaiting completion
- File operations with
tokio::fs - Non-blocking I/O operations
- Error handling in async contexts
- Async-safe mutexes (
tokio::sync::Mutex) - When to use async vs standard mutexes
- Shared state management across tasks
- Multi-producer, single-consumer channels
- Task-to-task communication
- Channel capacity and backpressure
- Setting deadlines for async operations
- Preventing operations from hanging indefinitely
- Using
tokio::time::timeoutfor graceful failure handling
- One-shot notifications between tasks
- Simple signaling without data transfer
- Using
tokio::sync::Notifyfor lightweight task coordination - Racing multiple futures with
tokio::select! - Handling the first completed operation among many
- Single-use channels for one-time value transfer
- Request-response patterns with
tokio::sync::oneshot
- Processing sequences of async values
- Using
tokio_streamfor handling collections of async data - Stream iteration with
StreamExt::next() - Converting iterators to async streams
- Manual runtime creation and management
- Bridging async and synchronous code
- Using
tokio::runtime::Builderfor custom runtime configuration - Running async code from non-async contexts
- Handling system signals asynchronously
- Graceful shutdown and cleanup
- Using
tokio::signalfor Ctrl+C and other signal processing - Cross-platform signal handling
- Cooperative cancellation of async tasks
- Using
tokio_util::sync::CancellationTokenfor graceful termination - Combining cancellation with
tokio::select!for responsive task management - Clean shutdown patterns for long-running operations
- Tracking multiple spawned tasks with
TaskTracker - Managing task lifecycle and completion
- Using
tracker.close()andtracker.wait()for coordinated task shutdown - Bulk task management for complex async workflows
- Combining
TaskTrackerwithCancellationTokenfor cancellable task groups - Using
tokio::select!to race between task execution and cancellation signals
- Writing unit tests for async functions
- Using
#[tokio::test]attribute for async test execution - Testing async operations with assertions
- Ensuring async code correctness through automated tests
- One-time async initialization of static values
- Using
tokio::sync::OnceCellfor lazy initialization - Thread-safe initialization that happens only once, even with concurrent access
- Deferring expensive async operations until first access
Each example builds upon the previous one:
- Start simple with basic task spawning
- Add timing to understand async vs blocking operations
- Introduce I/O to see async file operations
- Handle state to learn about async-safe synchronization
- Communicate between tasks using channels
- Add timeouts to prevent operations from hanging indefinitely
- Create intervals to run periodic async tasks
- Implement Futures to understand low-level async mechanics
- Coordinate tasks using lightweight notifications
- Race operations to handle the first completed future
- Transfer values using single-use channels
- Process sequences of async values with streams
- Bridge contexts between async and synchronous code
- Handle signals for graceful shutdown and system interaction
- Cancel tasks cooperatively for responsive application control
- Track tasks to manage multiple spawned operations and wait for completion
- Test async code to ensure correctness and reliability
- Combine patterns by integrating task tracking with cancellation for robust task management
- Initialize lazily with async one-time initialization for static values
- Task spawning:
tokio::spawn(async { ... }) - Async sleep:
tokio::time::sleep(Duration::from_secs(n)).await - File operations:
tokio::fs::read_to_string()andtokio::fs::write() - Async mutexes:
tokio::sync::Mutexwith.lock().await - Channels:
mpsc::channel(n)for sender/receiver communication - Timeouts:
tokio::time::timeout(Duration::from_secs(n), future).await - Intervals:
tokio::time::interval(Duration::from_secs(n))for periodic tasks - Custom Futures: Implement
Futuretrait withpoll()method and waker handling - Notifications:
tokio::sync::Notifyfor one-shot task signaling - Select macro:
tokio::select!for racing multiple async operations - One-shot channels:
tokio::sync::oneshot::channel()for single-use value transfer - Streams:
tokio_stream::iter()andStreamExt::next()for processing async sequences - Runtime bridging:
tokio::runtime::Builder::new_multi_thread().build().unwrap().block_on()for running async code from sync contexts - Signal handling:
tokio::signal::ctrl_c().awaitfor graceful shutdown and signal processing - Cancellation:
CancellationToken::new()andtoken.cancelled().awaitfor cooperative task cancellation - Task tracking:
TaskTracker::new()andtracker.spawn()for managing multiple task lifecycles - Combined patterns: Using
TaskTrackerwithCancellationTokenandtokio::select!for cancellable task groups - Lazy initialization:
OnceCell::const_new()and.get_or_init()for one-time async initialization of static values - Async testing:
#[tokio::test]attribute for writing unit tests for async functions
This project includes a structured logging setup using the tracing ecosystem:
The get_logger() function provides a pre-configured tracing subscriber with:
- Compact format for cleaner log output
- File paths and line numbers for easy debugging
- Thread IDs to track concurrent operations
- No target paths for reduced clutter
All binary examples can use the shared logger configuration:
use hello_tokio_async_rust::utils::get_logger;
#[tokio::main]
async fn main() {
let sub = get_logger();
tracing::subscriber::set_global_default(sub).unwrap();
tracing::info!("Application started");
}The lib.rs file exposes the utils module, making it accessible to all binary files in the project.
This is a personal learning project, but feel free to:
- Suggest improvements to examples
- Add new async concepts
- Improve documentation
- Report issues or bugs
Happy Learning! 🦀✨
This project demonstrates the power and elegance of async Rust with Tokio, showing how to build efficient, concurrent applications without the complexity of manual thread management.