You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// For shared state across tasks, these are your main tools:structMyService{// Immutable shared stateconfig:Arc<Config>,// Mutable shared statestate:Arc<Mutex<State>>,}// Usage example:implMyService{asyncfnprocess(&self){let state = self.state.lock().await;// Work with state...}asyncfnspawn_worker(&self){let state = self.state.clone();let config = self.config.clone();
tokio::spawn(asyncmove{// Work with state and config...});}}
Common Gotchas
1. Task Cancellation
// ❌ BAD: Counter may never decrement if task is cancelledasyncfnwrong_task(counter:Arc<AtomicUsize>){
counter.fetch_add(1,Ordering::Relaxed);do_something().await;
counter.fetch_sub(1,Ordering::Relaxed);// Might never run!}// ✅ GOOD: Use drop guardasyncfncorrect_task(counter:Arc<AtomicUsize>){
counter.fetch_add(1,Ordering::Relaxed);let _guard = scopeguard::guard((), |_| {
counter.fetch_sub(1,Ordering::Relaxed);});do_something().await;}
2. Select Loop Future Recreation
// ❌ BAD: Creates new futures on every loop iterationasyncfnwrong_select(mutrx: mpsc::Receiver<()>){loop{select!{
_ = rx.recv() => {},
_ = tokio::time::sleep(Duration::from_secs(1)) => {},}}}// ✅ GOOD: Create futures once outside the loopasyncfncorrect_select(mutrx: mpsc::Receiver<()>){letmut recv = rx.recv().fuse();letmut timer = tokio::time::sleep(Duration::from_secs(1)).fuse();pin_mut!(recv, timer);loop{select!{
_ = &mut recv => {},
_ = &mut timer => {},}}}
// ❌ BAD: Blocks the async runtimeasyncfnwrong_heavy_task(){let result = expensive_computation();use_result(result).await;}// ✅ GOOD: Use spawn_blockingasyncfncorrect_heavy_task(){let result = tokio::task::spawn_blocking(|| {expensive_computation()}).await.unwrap();use_result(result).await;}
5. RAII Guards Across Await
// ❌ BAD: Holds pool connection during unrelated workasyncfnwrong_pool_usage(pool:&Pool){let conn = pool.get().await;do_db_work(&conn).await;do_unrelated_work().await;// Still holding connection!do_more_db_work(&conn).await;}// ✅ GOOD: Release resources when not neededasyncfncorrect_pool_usage(pool:&Pool){// Scope 1{let conn = pool.get().await;do_db_work(&conn).await;}// Connection released heredo_unrelated_work().await;// Scope 2{let conn = pool.get().await;do_more_db_work(&conn).await;}}
Best Practices
Always use spawn_blocking for CPU-intensive work
Think about cancellation at every .await point
Use drop guards for cleanup code
Don't hold RAII guards across .await points
Spawn tasks for independent concurrent work
Create long-lived futures outside loops
Use fuse() and pin_mut! for select loops
Consider if you really need async at all - threads might be simpler!