fearless concurrency â Rust çæææç³»ç»åšçŒè¯ææ¶çæ°æ®ç«äºïŒè®©å¹¶åä»"å°å¿ç¿Œç¿Œ"åæ"æŸå¿å€§è"ãä» OS 线çšå° async/awaitïŒä» Mutex å° tokioïŒè¿ç¯æç« èŠç Rust å¹¶åç宿ŽåŸæ¯ã
åŸå€äººæ··çš"å¹¶å"å"å¹¶è¡"ïŒäœå®ä»¬æ¯äžåçæŠå¿µïŒ
å€äžªä»»å¡åšé»èŸäžåæ¶æšè¿
å€äžªä»»å¡åšç©çäžåæ¶æ§è¡
ð¯ å ³é®æŽå¯ïŒå¹¶åæ¯å ³äºè®Ÿè®¡çïŒå¹¶è¡æ¯å ³äºæ§è¡çãäœ å¯ä»¥æå¹¶åæ å¹¶è¡ïŒåæ žå€ä»»å¡ïŒïŒä¹å¯ä»¥æå¹¶è¡æ å¹¶åïŒç¬ç«è¿çšåå¹²åçïŒãæå¥œçæ åµïŒå¹¶å讟计 + å¹¶è¡æ§è¡ã
Rust æåŒºå€§çäžç¹ïŒå®æå¹¶åå®å 𿣿¥åç§»å°äºçŒè¯æãäžæ¯è¿è¡æ¶æ¥éïŒäžæ¯æµè¯æ¶åç°ïŒèæ¯çŒè¯éœè¿äžäºãäž€äžªæ žå¿ marker traitïŒ
ç±»å T å®ç°äº SendïŒè¡šç€ºå¯ä»¥å®å
šå°è·šçº¿çšç§»åšæææã倧倿°ç±»åèªåšå®ç° SendïŒäŸå€ïŒRc<T>ãRawFd çã
Send åŒå¯ä»¥ä»çº¿çš A move å°çº¿çš B
ç±»å T å®ç°äº SyncïŒè¡šç€ºå¯ä»¥å®å
šå°è·šçº¿çšå
±äº«åŒçšïŒå³ &T æ¯ Send çïŒãCell<T>ãRefCell<T> äžå®ç° Syncã
Sync &T å¯ä»¥ä»çº¿çš A å
±äº«ç»çº¿çš B
// Send/Sync æ¯ auto trait ââ çŒè¯åšèªåšæšå¯Œ
// è§ååŸç®åïŒææåæ®µéœ Send â ç»æäœ SendïŒææåæ®µéœ Sync â ç»æäœ Sync
struct Safe { data: Vec<String> } // â
èªåš Send + Sync
struct NotSync { cell: Cell<usize> } // â äžå®ç° SyncïŒCell äžæ¯ SyncïŒ
struct NotSend { rc: Rc<String> } // â äžå®ç° SendïŒRc äžæ¯ SendïŒ
// æåšæ è®°ïŒä»
åšç¡®å®å®å
šæ¶äœ¿çšïŒïŒ
struct ForceSend(*const u8);
unsafe impl Send for ForceSend {} // unsafeïŒäœ åçŒè¯åšä¿è¯å®æ¯å®å
šç
â ïž unsafe impl Send/Sync æ¯äœ åçŒè¯åšäžç"å什ç¶"ãçŒè¯åšäžåæ£æ¥ïŒäžååæèªèŽã倧倿°æ¶åäœ äžéèŠæåšå®ç°ââåŠæçŒè¯åšäžè®©äœ SendïŒå®å€§æŠçæ¯å¯¹çã
Rust çåççº¿çšæ¯ OS 线çšïŒ1:1 æš¡åïŒïŒç±æäœç³»ç»çŽæ¥è°åºŠãå建线çšçš std::thread::spawnïŒ
use std::thread;
use std::time::Duration;
fn main() {
// spawn è¿å JoinHandle<T>
let handle = thread::spawn(|| {
for i in 1..=5 {
println!("å线çš: 第 {} 次æå°", i);
thread::sleep(Duration::from_millis(100));
}
"å线çšå®æ" // è¿ååŒ
});
// 䞻线çšç»§ç»æ§è¡
for i in 1..=3 {
println!("䞻线çš: 第 {} 次æå°", i);
thread::sleep(Duration::from_millis(100));
}
// join çåŸ
å线çšç»æïŒè·åè¿ååŒ
let result = handle.join().unwrap();
println!("å线çšè¿å: {}", result);
}
éå
é»è®€æåŒçšæè·åéïŒäœçº¿çšå¯èœæ¯å建å®çäœçšåæŽ»åŸæŽä¹
ãRust èŠæ±è·šçº¿çšéå
å¿
é¡» moveïŒææææèœ¬ç§»å°æ°çº¿çšïŒ
use std::thread;
fn main() {
let name = String::from("Alice");
let age = 30;
// â çŒè¯é误ïŒname çåŒçšå¯èœåšäž»çº¿çšéæŸåä»åšå线çšäžäœ¿çš
// thread::spawn(|| println!("Hello, {}", name));
// â
move æ name å age çæææèœ¬ç§»å°å线çš
let handle = thread::spawn(move || {
println!("Hello, {}! You are {} years old.", name, age);
// name å age åšè¿é被æ¶è޹ïŒäž»çº¿çšäžèœå䜿çš
});
handle.join().unwrap();
}
åŠäœææ°æ®ä»å建è äŒ ç»æ°çº¿çšïŒäœåäžæ³ move æŽäžªåéïŒå ç§æ¹åŒïŒ
| æ¹åŒ | åºæ¯ | åŒé |
|---|---|---|
move éå
| æææèœ¬ç§»ïŒç®åçŽæ¥ | é¶åŒé |
channel | æµåŒäŒ éïŒç产è -æ¶è޹è | äžæ¬¡ clone |
Arc<T> | å ±äº«åªè¯»æ°æ® | åååŒçšè®¡æ° |
thread::scope | 䞎æ¶çº¿çšïŒåŒçšå€å±åé | é¶åŒé |
Rust 1.63 åŒå
¥ç thread::scope è§£å³äºç»å
žçç¹ïŒåŠäœåšå线çšäžåŒçšå€å±åéïŒèäžçšæ
å¿çåœåšæïŒscope ä¿è¯ææå线çšåšäœçšåç»æåéåºïŒ
use std::thread;
fn main() {
let data = vec![1, 2, 3, 4, 5, 6, 7, 8];
// scope å
å建ç线çšïŒåš scope ç»ææ¶èªåš join
thread::scope(|s| {
// åçåŒçšå¯ä»¥å®å
šäŒ å
¥ïŒå 䞺 scope ä¿è¯çº¿çšåš scope ç»æåéåº
let chunk1 = &data[0..4];
let chunk2 = &data[4..8];
s.spawn(|| {
let sum: i32 = chunk1.iter().sum();
println!("ååæ±å: {}", sum);
});
s.spawn(|| {
let sum: i32 = chunk2.iter().sum();
println!("ååæ±å: {}", sum);
});
}); // â è¿éèªåš join ææçº¿çšïŒdata äžäŒæ¬ç©º
println!("å
šéšå®æïŒdata ä»å¯çš: {:?}", data);
}
æåšç®¡ç线çšåŸç¹çïŒrayon æäŸäºå·¥äœçªåïŒwork-stealingïŒçº¿çšæ± ïŒè®©æ°æ®å¹¶è¡ååŸæå
¶ç®åïŒ
use rayon::prelude::*; // åŒå
¥å¹¶è¡è¿ä»£åš trait
fn main() {
let data: Vec<u64> = (1..=10_000_000).collect();
// å线çšè¿ä»£
let sum_seq: u64 = data.iter().sum();
// å¹¶è¡è¿ä»£ ââ åªéæ iter() æ¹æ par_iter()
let sum_par: u64 = data.par_iter().sum();
assert_eq!(sum_seq, sum_par);
// æŽå®çšçäŸåïŒå¹¶è¡å€ç
let results: Vec<String> = data
.par_iter()
.filter(|&&x| x % 2 == 0)
.map(|&x| format!("even: {}", x))
.collect();
}
â
rayon çéåïŒæ iter() æ¹æ par_iter()ïŒå
¶ä»ä»£ç äžè¡äžçšæ¹ãrayon èªåšæä»»å¡åé
å°çº¿çšæ± ïŒå€çå·¥äœçªåãèŽèœœåè¡¡åç»ææ¶éãåšå€æ žæºåšäžéåžžèœè·åŸæ¥è¿çº¿æ§çå 鿝ã
Rust ç€Ÿåºæäžäžªèåçå£å·ïŒ"Do not communicate by sharing memory; instead, share memory by communicating." æ¶æ¯äŒ 鿝 Rust å¹¶åçæ žå¿èåŒä¹äžã
mpsc = Multi-Producer, Single-Consumerãäžäžªæ¥æ¶è
ïŒå€äžªåéè
ïŒ
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn main() {
// channel() è¿å (Sender, Receiver)
let (tx, rx) = mpsc::channel();
// å€ç产è
ïŒclone sender
let tx1 = tx.clone();
let tx2 = tx.clone();
// ç产è
1
thread::spawn(move || {
let msgs = vec!["ä»»å¡A-1", "ä»»å¡A-2", "ä»»å¡A-3"];
for msg in msgs {
tx1.send(msg).unwrap();
thread::sleep(Duration::from_millis(100));
}
});
// ç产è
2
thread::spawn(move || {
let msgs = vec!["ä»»å¡B-1", "ä»»å¡B-2"];
for msg in msgs {
tx2.send(msg).unwrap();
thread::sleep(Duration::from_millis(150));
}
});
// drop åå§ senderïŒåŠå rx.iter() äŒæ°žè¿é»å¡
drop(tx);
// æ¶è޹è
ïŒè¿ä»£æ¥æ¶çŽå°ææ sender 被 drop
for received in rx.iter() {
println!("æ¶å°: {}", received);
}
println!("ææç产è
å·²æåŒïŒæ¥æ¶ç»æ");
}
let (tx, rx) = mpsc::channel(); // æ mpsc::unbounded() // â send æ°žè¿äžé»å¡ // â ïž åŠææ¶èŽ¹è æ ¢ïŒå åäŒæ éå¢é¿ // éåïŒç产è éç坿§ãæ¶è޹é床皳å®
let (tx, rx) = mpsc::sync_channel(100); // 容é䞺 100 // â æ»¡äºä¹å send äŒé»å¡ïŒèåïŒïŒ // â 鲿¢å åæ éå¢é¿ // éåïŒç产è éçäžç¡®å®ãéèŠèå // send_timeout() å¯è®Ÿè¶ æ¶ // try_send() å¯éé»å¡åé
crossbeam-channel æ¯æ ååº mpsc ç髿§èœæ¿ä»£ïŒ
use crossbeam_channel as channel;
use std::thread;
fn main() {
// unbounded
let (tx, rx) = channel::unbounded();
// boundedïŒæ¯æ select!ïŒ
let (tx_b, rx_b) = channel::bounded(100);
// crossbeam ç Receiver å®ç°äº Iterator
// æŽéèŠçæ¯ïŒSender å Receiver éœæ¯ Send + Sync
// å¯ä»¥åšå€çº¿çšéŽèªç±å
񄧮
thread::spawn(move || {
tx.send("hello from crossbeam").unwrap();
});
// recv() é»å¡æ¥æ¶ïŒtry_recv() éé»å¡
let msg = rx.recv().unwrap();
println!("{}", msg);
// select! ââ åæ¶çåŸ
å€äžª channel
let (tx1, rx1) = channel::bounded(1);
let (tx2, rx2) = channel::bounded(1);
crossbeam_channel::select! {
recv(rx1) -> msg => println!("ä» rx1 æ¶å°: {:?}", msg),
recv(rx2) -> msg => println!("ä» rx2 æ¶å°: {:?}", msg),
}
}
| ç¹æ§ | std::sync::mpsc | crossbeam-channel |
|---|---|---|
| æ§èœ | äžç | æŽå¿«ïŒæ éå éšå®ç°ïŒ |
| Sender Clone | â | â |
| Receiver Clone | âïŒåæ¶è޹è ïŒ | â ïŒå€æ¶è޹è MPMCïŒ |
| select! | â | â |
| bounded + unbounded | â | â |
| Send + Sync | ä» Sender | Sender + Receiver |
| é¢å€äŸèµ | æ | éå crate |
åœå€äžªçº¿çšéèŠä¿®æ¹åäžä»œæ°æ®æ¶ïŒMutex<T> æ¯æçŽæ¥çå·¥å
·ãRust ç Mutex 讟计å²åŠïŒæ°æ®äžéç»å®ïŒäžæ¿å°é就访é®äžå°æ°æ®ïŒ
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
// Mutex å
裹éèŠä¿æ€çæ°æ®
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
// lock() è¿å MutexGuardïŒèªåš unlock
let mut num = counter.lock().unwrap();
*num += 1;
// MutexGuard åšè¿é dropïŒèªåšéæŸé
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("ç»æ: {}", *counter.lock().unwrap()); // 10
}
ð¯ Rust Mutex ç粟åŠè®Ÿè®¡ïŒéåæ°æ®ç»å®åšäžèµ·ââMutex<T> æ¥ææ°æ®ãäœ æ æ³åšäž lock() çæ
åµäžè®¿é®å
éšç Tãè¿æ¯ C ç pthread_mutexïŒéåæ°æ®æ¯å犻çïŒå¿äºå éçŽæ¥è®¿é®ä¹äžäŒæ¥éïŒå®å
šåŸå€ã
读å€åå°çåºæ¯ïŒRwLock å
讞å€äžªè¯»è
åæ¶ææéïŒäœåè
ç¬å ïŒ
use std::sync::{Arc, RwLock};
use std::thread;
fn main() {
let data = Arc::new(RwLock::new(vec![1, 2, 3]));
// å€äžªè¯»è
å¯ä»¥åæ¶ææ read lock
let mut readers = vec![];
for i in 0..5 {
let data = Arc::clone(&data);
readers.push(thread::spawn(move || {
let r = data.read().unwrap(); // RwLockReadGuard
println!("读è
{}: {:?}", i, *r);
}));
}
// åè
éèŠç¬å 访é®
let data_w = Arc::clone(&data);
let writer = thread::spawn(move || {
let mut w = data.write().unwrap(); // RwLockWriteGuard
w.push(4);
println!("åè
: æ·»å äºå
çŽ ");
});
for r in readers { r.join().unwrap(); }
writer.join().unwrap();
println!("æç»æ°æ®: {:?}", *data.read().unwrap());
}
Arc = Atomic Reference Countedã线çšå®å
šç RcïŒçšäºå€çº¿çšå
±äº«æææïŒ
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
// Arc: 让å€äžªçº¿çš"å
±åæ¥æ"åäžäžª Mutex
// Mutex: ä¿æ€å
éšæ°æ®çå®å
šè®¿é®
let shared = Arc::new(Mutex::new(Vec::new()));
let mut handles = vec![];
for i in 0..4 {
let shared = Arc::clone(&shared); // åŒçšè®¡æ° +1ïŒååæäœïŒ
handles.push(thread::spawn(move || {
let mut data = shared.lock().unwrap();
data.push(format!("æ¥èªçº¿çš {}", i));
}));
}
for h in handles { h.join().unwrap(); }
println!("{:?}", *shared.lock().unwrap());
}
对äºç®åç计æ°åšãæ å¿äœïŒAtomic ç±»åæ¯ Mutex æŽé«æââäžéèŠè·åéïŒçŽæ¥çš CPU çååæä»€ïŒ
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
fn main() {
let counter = Arc::new(AtomicUsize::new(0));
let done = Arc::new(AtomicBool::new(false));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
let done = Arc::clone(&done);
handles.push(thread::spawn(move || {
// fetch_add è¿åæ§åŒïŒååå°æ§è¡ += 1
counter.fetch_add(1, Ordering::SeqCst);
}));
}
for h in handles { h.join().unwrap(); }
// load ååå°è¯»ååŒ
println!("计æ°åš: {}", counter.load(Ordering::SeqCst)); // 10
// compare_exchange ââ CAS æäœ
let old = 0;
let new = 42;
counter.compare_exchange(old, new, Ordering::SeqCst, Ordering::SeqCst);
}
| Ordering | ä¿è¯ | æ§èœ | 䜿çšåºæ¯ |
|---|---|---|---|
Relaxed | æ 顺åºä¿è¯ïŒä» ååæ§ | æå¿« | 计æ°åšãç»è®¡ |
Acquire / Release | Release ä¹åçå对 Acquire ä¹åç读å¯è§ | äžç | éãflag éç¥ |
AcqRel | Acquire + Release | äžç | read-modify-write |
SeqCst | å šå±é¡ºåºäžèŽ | ææ ¢ | éèŠäž¥æ Œé¡ºåºæ¶ |
â ïž å®çšå»ºè®®ïŒäžç¡®å®çšä»ä¹ Ordering æ¶ïŒçš SeqCstãæ§èœå·®åŒåšç»å€§å€æ°åºæ¯äžå¯ä»¥å¿œç¥ãåªæåœäœ profiling åç°ååæäœæ¯ç¶é¢ æ¶ïŒæèèéçº§å°æŽå®œæŸç Orderingã
å€äžªçº¿çšéèŠåšæäžªç¹æ±åååäžèµ·ç»§ç»ïŒçš BarrierïŒ
use std::sync::{Arc, Barrier};
use std::thread;
fn main() {
let n_threads = 4;
let barrier = Arc::new(Barrier::new(n_threads));
let mut handles = vec![];
for i in 0..n_threads {
let barrier = Arc::clone(&barrier);
handles.push(thread::spawn(move || {
println!("çº¿çš {} é¶æ®µ1 宿", i);
// ææçº¿çšéœå°è¿éåïŒäžèµ·ç»§ç»
barrier.wait();
println!("çº¿çš {} é¶æ®µ2 åŒå§", i);
}));
}
for h in handles { h.join().unwrap(); }
}
åœçº¿çšéèŠçåŸ
æäžªæ¡ä»¶æç«æ¶ïŒCondvar + Mutex æ¯ç»å
žç»åïŒ
use std::sync::{Arc, Mutex, Condvar};
use std::thread;
use std::time::Duration;
fn main() {
// Condvar æ»æ¯å Mutex é
对䜿çš
let pair = Arc::new((Mutex::new(false), Condvar::new()));
let pair2 = Arc::clone(&pair);
// çåŸ
线çš
let waiter = thread::spawn(move || {
let (lock, cvar) = &*pair2;
let mut started = lock.lock().unwrap();
// wait äŒïŒ1. éæŸé 2. æèµ· 3. 被 notify åéæ°è·åé
while !*started {
started = cvar.wait(started).unwrap();
}
println!("çåŸ
线çš: æ¡ä»¶æ»¡è¶³ïŒç»§ç»æ§è¡");
});
// éç¥çº¿çš
thread::spawn(move || {
let (lock, cvar) = &*pair;
thread::sleep(Duration::from_secs(1));
{
let mut started = lock.lock().unwrap();
*started = true;
}
// notify_one å€éäžäžªçåŸ
线çš
cvar.notify_one();
// cvar.notify_all() å€éææçåŸ
线çš
}).join().unwrap();
waiter.join().unwrap();
}
å šå±åªåå§åäžæ¬¡çåŒïŒåäŸæš¡åŒãå šå±é 眮ïŒïŒRust æäŸäºå®å šçåè¯ïŒ
use std::sync::{OnceLock, Once};
use std::thread;
// OnceLock: å»¶è¿åå§åçå
šå±åé
static DATABASE: OnceLock<String> = OnceLock::new();
fn get_database() -> &'static str {
DATABASE.get_or_init(|| {
println!("åå§åæ°æ®åºè¿æ¥...");
String::from("postgresql://localhost:5432/mydb")
})
}
// Once: åªæ§è¡äžæ¬¡ç代ç å
static INIT: Once = Once::new();
fn initialize() {
INIT.call_once(|| {
println!("è¿æ®µä»£ç å
šå±åªæ§è¡äžæ¬¡");
});
}
fn main() {
// å€çº¿çšåæ¶è°çš get_databaseïŒåªæç¬¬äžäžªäŒçæ£åå§å
let mut handles = vec![];
for _ in 0..5 {
handles.push(thread::spawn(|| {
let db = get_database();
println!("è·åå°: {}", db);
}));
}
for h in handles { h.join().unwrap(); }
}
â
OnceLock vs lazy_static / once_cellïŒRust 1.70 æ OnceLock çš³å®è¿äºæ ååºïŒåºæ¬å¯ä»¥æ¿ä»£ lazy_static! å once_cell::sync::LazyLockã对äºç®ååºæ¯äŒå
çšæ ååºã
OS 线çšçé®é¢ïŒ
Async ççæ¡ïŒèœ»é级任å¡ïŒåšçšæ·æè°åºŠïŒäžäžªä»»å¡åªéå çŸåèïŒèœ»æŸæ¯æçŸäžå¹¶åã
线çš1 ââ[çåŸ I/O]âââ é»å¡ïŒå æ žè°åºŠåšæèµ·ïŒ 线çš2 ââ[çåŸ I/O]âââ é»å¡ 线çš3 ââ[计ç®äž]âââ è¿è¡ å å: æ¯çº¿çš ~2MB æ 忢: å æ žæ (~1-10ÎŒs) è§æš¡: å åçº¿çš éå: CPU å¯éå
ä»»å¡1 ââ[çåŸ I/O]âââ æèµ·ïŒçšæ·æ yieldïŒ ä»»å¡2 ââ[çåŸ I/O]âââ æèµ· ä»»å¡3 ââ[计ç®äž]âââ è¿è¡ å å: æ¯ä»»å¡ ~å çŸåè 忢: çšæ·æ (~0.2-1ÎŒs) è§æš¡: çŸäžä»»å¡ éå: I/O å¯éå
äžååŒæ¥çåºç¡æ¯ Future traitïŒ
pub trait Future {
type Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}
pub enum Poll<T> {
Ready(T), // 宿ïŒè¿åç»æ
Pending, // æªå®æïŒçšåå poll
}
å
³é®çè§£ïŒFuture æ¯æ°æ§çââå建å®äžäŒæ§è¡ä»»äœä»£ç ãå¿
须被 poll æäŒæšè¿ãåœäœ .await æ¶ïŒå®é
äžæ¯ïŒ
PendingïŒæ³šå WakerïŒåœåä»»å¡æèµ·Ready(value)
// async fn è¿åäžäžª impl Future
async fn fetch_user(id: u32) -> User {
let response = http_get(format!("/api/users/{}", id)).await; // çåŸ
I/O
let user: User = response.json().await; // çåŸ
è§£æ
user // è¿ååŒ
}
// çä»·äºïŒ
fn fetch_user(id: u32) -> impl Future<Output = User> {
async move {
let response = http_get(format!("/api/users/{}", id)).await;
let user: User = response.json().await;
user
}
}
// async block ââ äžéèŠåœæ°ïŒçŽæ¥å
let future = async {
let a = fetch_user(1).await;
let b = fetch_user(2).await;
(a, b)
};
async fn çŒè¯åçæäžäžªç¶ææºãåŠæ Future åš .await ç¹ä¹éŽä¿åäºåŒçšïŒæåèªèº«æ°æ®ïŒïŒé£ä¹ç§»åšè¿äžª Future äŒè®©åŒçšæ¬ç©ºãPin å°±æ¯ä¿è¯"äžæŠåŒå§ pollïŒå°±äžèœç§»åš"ïŒ
åœ Future è¿å PendingïŒå®éèŠåè¯è¿è¡æ¶"ä»ä¹æ¶åå poll æ"ãè¿å°±æ¯ Waker çäœçšïŒ
// åœ Future è¿å Pending æ¶ïŒ
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Output> {
if self.is_ready() {
Poll::Ready(self.take_output())
} else {
// æ waker 泚åå° I/O æ¥æºïŒepoll/kqueue/...ïŒ
// åœ I/O 就绪æ¶ïŒepoll äŒè°çš waker.wake()
// wake() éç¥è¿è¡æ¶éæ° poll è¿äžª Future
self.register_waker(cx.waker());
Poll::Pending
}
}
// æµçšïŒ
// 1. è¿è¡æ¶ poll Future â Pending
// 2. Future 泚å Waker
// 3. I/O 就绪 â Waker.wake() 被è°çš
// 4. è¿è¡æ¶éæ° poll Future â Ready æ PendingïŒåŸªç¯ïŒ
ð¯ æ¥åžžåŒåäžéèŠæåšå®ç° Future/Pin/Wakerã99% çåºæ¯äžäœ åªéèŠå async fn å .awaitïŒè¿è¡æ¶ïŒtokio/async-stdïŒäŒå€çææåºå±ç»èãçè§£è¿äºæŠå¿µæ¯äžºäºåºé®é¢æ¶èœçææ¥éïŒèäžæ¯æ¥åžžæå pollã
tokio æ¯ Rust æäž»æµçåŒæ¥è¿è¡æ¶ïŒçžåœäºåŒæ¥äžçç"æäœç³»ç»"ââå®ç®¡çä»»å¡è°åºŠãI/O 驱åšã计æ¶åšçïŒ
#[tokio::main] // æåžžçšïŒå€çº¿çšè¿è¡æ¶
async fn main() {
println!("Hello, tokio!");
}
// çä»·äºæåšå建ïŒ
fn main() {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
println!("Hello, tokio!");
});
}
// äž€ç§ RuntimeïŒ
// 1. å€çº¿çšïŒé»è®€ïŒââ éå倧倿°åºæ¯ïŒå·¥äœçªåè°åºŠ
#[tokio::main]
async fn main() { /* ... */ }
// 2. åœåçº¿çš ââ éå蜻é/åµå
¥åŒåºæ¯
#[tokio::main(flavor = "current_thread")]
async fn main() { /* ... */ }
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() {
// spawn å建äžäžªåŒæ¥ä»»å¡ïŒè¿å JoinHandle
let handle = tokio::spawn(async {
sleep(Duration::from_secs(1)).await;
println!("åŒæ¥ä»»å¡å®æ");
42
});
// åå
¶ä»äºæ
...
println!("䞻任å¡ç»§ç»æ§è¡");
// await JoinHandle è·åç»æ
let result = handle.await.unwrap(); // 泚æïŒ.await äžæ¯ .join()
println!("ç»æ: {}", result);
}
â ïž 'static 纊æïŒtokio::spawn èŠæ± Future æ¯ 'static çââäžèœåçšå€éšåéãéèŠçš move èœ¬ç§»æææïŒæè
çš Arc å
±äº«ã
select! æ¯ tokio æåŒºå€§çç»ååšä¹äžïŒåæ¶çåŸ
å€äžªåŒæ¥æäœïŒè°å
宿就å€çè°ïŒ
use tokio::sync::mpsc;
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() {
let (tx1, mut rx1) = mpsc::channel(32);
let (tx2, mut rx2) = mpsc::channel(32);
// å¯åšäž€äžªç产è
tokio::spawn(async move {
tx1.send("æ¶æ¯æ¥èª channel 1").await.unwrap();
});
tokio::spawn(async move {
sleep(Duration::from_millis(50)).await;
tx2.send("æ¶æ¯æ¥èª channel 2").await.unwrap();
});
// select! è°å
å°å€çè°
tokio::select! {
msg = rx1.recv() => {
println!("æ¶å° rx1: {:?}", msg);
}
msg = rx2.recv() => {
println!("æ¶å° rx2: {:?}", msg);
}
}
// select! åŸªç¯ ââ å€çå€äžªäºä»¶æµ
loop {
tokio::select! {
msg = rx1.recv() => {
match msg {
Some(m) => println!("rx1: {}", m),
None => break, // channel å
³é
}
}
msg = rx2.recv() => {
match msg {
Some(m) => println!("rx2: {}", m),
None => break,
}
}
}
}
}
use tokio::task::JoinSet;
#[tokio::main]
async fn main() {
let mut set = JoinSet::new();
// æ¹é spawn
for i in 0..5 {
set.spawn(async move {
tokio::time::sleep(Duration::from_millis(i * 100)).await;
format!("ä»»å¡ {} 宿", i)
});
}
// æå®æé¡ºåºæ¶éç»æ
while let Some(result) = set.join_next().await {
println!("{}", result.unwrap());
}
}
tokio ä»»å¡çåæ¶éè¿ AbortHandle æ CancellationToken å®ç°ïŒ
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() {
// æ¹åŒ1: AbortHandle
let handle = tokio::spawn(async {
loop {
sleep(Duration::from_secs(1)).await;
println!("å·¥äœäž...");
}
});
let abort_handle = handle.abort_handle();
// 5ç§ååæ¶
sleep(Duration::from_secs(5)).await;
abort_handle.abort(); // æ handle.abort()
let _ = handle.await; // Err(JoinError::Cancelled)
// æ¹åŒ2: select! + è¶
æ¶ïŒæŽåžžçšïŒ
let result = tokio::time::timeout(
Duration::from_secs(3),
some_async_operation()
).await;
match result {
Ok(value) => println!("宿: {:?}", value),
Err(_) => println!("è¶
æ¶åæ¶"),
}
}
Rust 没æå 眮 Actor æ¡æ¶ïŒäžå Erlang/AkkaïŒïŒäœçš channel å¯ä»¥äŒé å°å®ç°ïŒ
use tokio::sync::{mpsc, oneshot};
// Actor çæ¶æ¯ç±»å
enum CounterMsg {
Increment,
Decrement,
Get(oneshot::Sender<i32>), // æºåžŠåå€ channel
}
// Actor ç»æäœ
struct CounterActor {
receiver: mpsc::Receiver<CounterMsg>,
count: i32,
}
impl CounterActor {
fn new(receiver: mpsc::Receiver<CounterMsg>) -> Self {
Self { receiver, count: 0 }
}
async fn run(&mut self) {
while let Some(msg) = self.receiver.recv().await {
match msg {
CounterMsg::Increment => self.count += 1,
CounterMsg::Decrement => self.count -= 1,
CounterMsg::Get(reply) => {
let _ = reply.send(self.count); // åå€åœååŒ
}
}
}
}
}
// Actor ç HandleïŒå¯¹å€æ¥å£ïŒ
#[derive(Clone)]
struct CounterHandle {
sender: mpsc::Sender<CounterMsg>,
}
impl CounterHandle {
fn new() -> Self {
let (sender, receiver) = mpsc::channel(128);
let mut actor = CounterActor::new(receiver);
tokio::spawn(async move { actor.run().await });
Self { sender }
}
async fn increment(&self) {
self.sender.send(CounterMsg::Increment).await.unwrap()
}
async fn get(&self) -> i32 {
let (tx, rx) = oneshot::channel();
self.sender.send(CounterMsg::Get(tx)).await.unwrap();
rx.await.unwrap()
}
}
#[tokio::main]
async fn main() {
let counter = CounterHandle::new();
counter.increment().await;
counter.increment().await;
println!("计æ°: {}", counter.get().await); // 2
}
Stream æ¯åŒæ¥çæ¬ç IteratorïŒåŒæ¯åŒæ¥äº§ççïŒ
use tokio_stream::{self as stream, StreamExt};
async fn stream_example() {
// ä»è¿ä»£åšå建 stream
let mut s = stream::iter(1..=5);
while let Some(value) = s.next().await {
println!("æ¶å°: {}", value);
}
// åžžçš stream ç»ååš
let results: Vec<i32> = stream::iter(1..=100)
.filter(|x| x % 2 == 0) // è¿æ»€å¶æ°
.map(|x| x * 3) // ä¹ä»¥3
.take(5) // åå5䞪
.collect()
.await;
// tokio æäŸç stream
// - LinesStream (æè¡è¯»æä»¶)
// - BroadcastStream (广æ channel)
// - WatchStream (watch channel)
// - IntervalStream (宿¶åš)
}
watch éå"é
çœ®æŽæ°"ã"ç¶æåæŽ"çåºæ¯ââåªä¿çææ°åŒïŒæ¶è޹è
å¯èœè·³è¿äžéŽåŒïŒ
use tokio::sync::watch;
#[tokio::main]
async fn main() {
let (tx, rx) = watch::channel("åå§é
眮".to_string());
// å€äžªæ¶è޹è
å¯ä»¥ clone Receiver
let mut rx1 = rx.clone();
let mut rx2 = rx.clone();
// æ¶è޹è
1
tokio::spawn(async move {
while rx1.changed().await.is_ok() {
let value = rx1.borrow();
println!("æ¶è޹è
1: {}", *value);
}
});
// æ¶è޹è
2
tokio::spawn(async move {
while rx2.changed().await.is_ok() {
let value = rx2.borrow();
println!("æ¶è޹è
2: {}", *value);
}
});
// ç产è
æŽæ°åŒ
tx.send("é
çœ®æŽæ°1".to_string()).unwrap();
tx.send("é
çœ®æŽæ°2".to_string()).unwrap();
tx.send("æç»é
眮".to_string()).unwrap();
}
æ§å¶å¹¶åæ°éïŒé²æ¢èµæºèå°œïŒ
use std::sync::Arc;
use tokio::sync::Semaphore;
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() {
const MAX_CONCURRENT: usize = 3;
let semaphore = Arc::new(Semaphore::new(MAX_CONCURRENT));
let mut handles = vec![];
for i in 0..10 {
let sem = Arc::clone(&semaphore);
handles.push(tokio::spawn(async move {
// è·å讞å¯ïŒåŠæå·²æ»¡åçåŸ
ïŒ
let _permit = sem.acquire().await.unwrap();
println!("ä»»å¡ {} åŒå§æ§è¡ïŒåœåå¹¶å: {}ïŒ", i, MAX_CONCURRENT - sem.available_permits());
sleep(Duration::from_secs(1)).await;
println!("ä»»å¡ {} 宿", i);
// _permit drop æ¶èªåšéæŸè®žå¯
}));
}
for h in handles { h.await.unwrap(); }
}
䞀䞪线çšåæ¶è®¿é®åäžå åïŒè³å°äžäžªåïŒæ²¡æåæ¥ã
static mutçšåºçæ£ç¡®æ§åå³äºæ¶åºïŒå³äœ¿æ²¡ææ°æ®ç«äºã
use std::sync::Mutex;
fn transfer(from: &Mutex<u64>, to: &Mutex<u64>, amount: u64) {
let mut from_guard = from.lock().unwrap();
// â ïž ç«ææ¡ä»¶ïŒåš check å use ä¹éŽïŒå
¶ä»çº¿çšå¯èœä¿®æ¹äº from
if *from_guard >= amount { // æ£æ¥
*from_guard -= amount; // 䜿çš
// åŠäžäžªçº¿çšåšè¿éä¹å¯èœéè¿äºåæ ·çæ£æ¥
drop(from_guard); // éæŸ from é
*to.lock().unwrap() += amount; // è·å to é
}
}
// 䞀䞪线çšåæ¶è°çš transfer(A, B, 100) å transfer(A, C, 100)
// åŠæ A äœé¢äžº 150ïŒäž€äžªçº¿çšéœéè¿äºæ£æ¥ â è¶
é¢èœ¬èŽŠïŒ
ç»å žæ»éåºæ¯ïŒäž€äžªçº¿çšåææäžæéïŒåæ¶çåŸ å¯¹æ¹çéïŒ
use std::sync::Mutex;
// æ¹æ¡1ïŒæåšä¿è¯é¡ºåº
fn transfer_safe(from: &Mutex<u64>, to: &Mutex<u64>, amount: u64,
from_id: usize, to_id: usize) {
// æ ID 顺åºè·åé
let (first, second) = if from_id < to_id {
(from, to)
} else {
(to, from)
};
let mut first_guard = first.lock().unwrap();
let mut second_guard = second.lock().unwrap();
let (from_ref, to_ref) = if from_id < to_id {
(&mut first_guard, &mut second_guard)
} else {
(&mut second_guard, &mut first_guard)
};
if **from_ref >= amount {
**from_ref -= amount;
**to_ref += amount;
}
}
// æ¹æ¡2ïŒçšäžæå€§éä¿æ€ææèŽŠæ·ïŒç®åäœå¹¶å床äœïŒ
// æ¹æ¡3ïŒçš channel/message passing æ¿ä»£é
parking_lot æäŸæ¯ std::sync æŽé«æçéå®ç°ïŒ
| ç¹æ§ | std::sync::Mutex | parking_lot::Mutex |
|---|---|---|
| éå€§å° | 40 bytes | 1 byte (ReentrantMutex é€å€) |
| å éåŒé | èŸé« | æŽäœ |
| äžæ¯ (Poison) | æïŒpanic æ¶æ è®°äžæ¯ïŒ | æ ïŒæŽç®åïŒ |
| å¯éå ¥ | â å线çšéå€éäŒæ»é | ReentrantMutex å¯éå ¥ |
| try_lock_for | â | â åžŠè¶ æ¶çå°è¯å é |
| Fair unlock | åŠ | å¯é |
use parking_lot::Mutex;
use std::sync::Arc;
fn main() {
// API å ä¹äžæ ·ïŒäœ lock() è¿å MutexGuardïŒäžéèŠ unwrapïŒïŒ
let data = Arc::new(Mutex::new(0));
let data2 = Arc::clone(&data);
std::thread::spawn(move || {
let mut guard = data2.lock(); // 没æ .unwrap()
*guard += 1;
// guard drop èªåšéæŸ
});
// 垊è¶
æ¶çå°è¯å é
if let Some(guard) = data.try_lock_for(std::time::Duration::from_secs(1)) {
println!("è·åéæå: {}", *guard);
} else {
println!("1ç§å
æªèœè·åé");
}
}
Rust ç RAII ä¿è¯äºéäžå®äŒéæŸïŒguard drop = unlockïŒãäœæ³šæïŒåŠæ guard ççåœåšææå€å»¶é¿ïŒæ¯åŠè¿å guardïŒïŒéäŒææå€ªä¹ ã
std::sync::Mutex åäžçº¿çšéå€ lock äŒæ»éãåŠæéèŠå¯éå
¥ïŒçš parking_lot::ReentrantMutexïŒäœèŠæ³æž
æ¥æ¯åŠè®Ÿè®¡æé®é¢ïŒã
MutexGuard äžæ¯ SendïŒäžèœè·š .await ææïŒçš tokio::sync::Mutex æ¿ä»£ïŒæè
ç¡®ä¿åš .await å drop æ guardã
åŸªç¯ try_lock() 浪莹 CPUãåºè¯¥çšé»å¡ç lock() æ Condvarã
绌åè¿çš tokio + Semaphore éæµ + Channel æ¶é + äŒé å ³éïŒæå»ºäžäžªç产级并åç¬è«ïŒ
[dependencies]
tokio = { version = "1", features = ["full"] }
reqwest = { version = "0.12", features = ["rustls-tls"] }
tokio-stream = "0.1"
use std::collections::HashSet;
use std::sync::Arc;
use tokio::sync::{mpsc, Semaphore, Mutex, Notify};
use tokio::time::{timeout, Duration};
/// ç¬åç»æ
#[derive(Debug)]
struct CrawlResult {
url: String,
status: u16,
body_len: usize,
links: Vec<String>,
}
/// ç¬è«é
眮
struct CrawlerConfig {
max_concurrent: usize,
request_timeout: Duration,
max_depth: u32,
max_urls: usize,
}
impl Default for CrawlerConfig {
fn default() -> Self {
Self {
max_concurrent: 10,
request_timeout: Duration::from_secs(10),
max_depth: 3,
max_urls: 100,
}
}
}
/// å¹¶åç¬è«
struct Crawler {
config: CrawlerConfig,
visited: Arc<Mutex<HashSet<String>>>,
semaphore: Arc<Semaphore>,
client: reqwest::Client,
shutdown: Arc<Notify>,
}
impl Crawler {
fn new(config: CrawlerConfig) -> Self {
let client = reqwest::Client::builder()
.timeout(config.request_timeout)
.user_agent("RustCrawler/1.0")
.build()
.unwrap();
Self {
semaphore: Arc::new(Semaphore::new(config.max_concurrent)),
visited: Arc::new(Mutex::new(HashSet::new())),
client,
shutdown: Arc::new(Notify::new()),
config,
}
}
/// å¯åšç¬è«ïŒè¿åææç»æ
async fn crawl(&self, seed_urls: Vec<String>) -> Vec<CrawlResult> {
let (url_tx, url_rx) = mpsc::channel<(String, u32)>(256);
let (result_tx, mut result_rx) = mpsc::channel(256);
// åéç§å URL
for url in seed_urls {
let _ = url_tx.send((url, 0)).await;
}
// å·¥äœçº¿çšïŒä» channel å URLïŒç¬åïŒåéç»æ
let worker_url_tx = url_tx.clone();
let visited = Arc::clone(&self.visited);
let semaphore = Arc::clone(&self.semaphore);
let client = self.client.clone();
let shutdown = Arc::clone(&self.shutdown);
let max_depth = self.config.max_depth;
let dispatcher = tokio::spawn(async move {
let mut url_rx = url_rx;
while let Some((url, depth)) = url_rx.recv().await {
// æ£æ¥æ¯åŠå·²è®¿é®
{
let mut vis = visited.lock().await;
if vis.contains(&url) {
continue;
}
vis.insert(url.clone());
}
// è·åä¿¡å·é讞å¯ïŒéå¶å¹¶åïŒ
let permit = semaphore.clone().acquire_owned().await.unwrap();
let client = client.clone();
let result_tx = result_tx.clone();
let url_tx = worker_url_tx.clone();
let visited = Arc::clone(&visited);
let max_urls = self.config.max_urls;
tokio::spawn(async move {
let _permit = permit; // ææè®žå¯çŽå°ä»»å¡å®æ
match timeout(
Duration::from_secs(10),
client.get(&url).send()
).await {
Ok(Ok(resp)) => {
let status = resp.status().as_u16();
let body = resp.text().await.unwrap_or_default();
let links = extract_links(&url, &body);
let result = CrawlResult {
url: url.clone(),
status,
body_len: body.len(),
links: links.clone(),
};
let _ = result_tx.send(result).await;
// åŠææ·±åºŠå
è®žïŒææ°éŸæ¥å å
¥éå
if depth < max_depth {
for link in links {
let vis = visited.lock().await;
if !vis.contains(&link) && vis.len() < max_urls {
drop(vis);
let _ = url_tx.send((link, depth + 1)).await;
}
}
}
}
Ok(Err(e)) => eprintln!("请æ±å€±èŽ¥ {}: {}", url, e),
Err(_) => eprintln!("è¶
æ¶: {}", url),
}
});
}
});
// äŒé
å
³éçæ§
let shutdown_clone = Arc::clone(&self.shutdown);
tokio::spawn(async move {
tokio::signal::ctrl_c().await.unwrap();
println!("\nâ ïž æ¶å° Ctrl+CïŒæ£åšäŒé
å
³é...");
shutdown_clone.notify_waiters();
});
// æ¶éç»æ
drop(result_tx); // å
³éåé端ïŒdispatcher ææäžä»œïŒ
let mut results = vec![];
while let Some(result) = result_rx.recv().await {
println!("â
[{}] {} ({} bytes, {} links)",
result.status, result.url, result.body_len, result.links.len());
results.push(result);
}
dispatcher.abort();
results
}
/// 请æ±äŒé
å
³é
fn shutdown(&self) {
self.shutdown.notify_waiters();
}
}
/// ä» HTML äžæåéŸæ¥ïŒç®åçïŒ
fn extract_links(base_url: &str, html: &str) -> Vec<String> {
let mut links = Vec::new();
for line in html.lines() {
if let Some(start) = line.find("href=\"") {
if let Some(rest) = line[start + 6..].split('"').next() {
if rest.starts_with("http") {
links.push(rest.to_string());
} else if rest.starts_with('/') {
links.push(format!("{}{}", base_url, rest));
}
}
}
}
links.dedup();
links
}
#[tokio::main]
async fn main() {
let crawler = Crawler::new(CrawlerConfig {
max_concurrent: 5,
max_depth: 2,
max_urls: 50,
..Default::default()
});
let results = crawler.crawl(vec![
"https://example.com".to_string(),
]).await;
println!("\nð ç¬å宿:");
println!(" æ»é¡µé¢æ°: {}", results.len());
println!(" æ»åèæ°: {}", results.iter().map(|r| r.body_len).sum::<usize>());
println!(" æåç: {:.1}%",
results.iter().filter(|r| r.status == 200).count() as f64
/ results.len() as f64 * 100.0
);
}
| å³ç | éæ© | åå |
|---|---|---|
| å¹¶åæ§å¶ | Semaphore | éå¶åæ¶è¯·æ±æ°ïŒé²æ¢æçç®æ æå¡åš |
| URL è°åºŠ | mpsc channel | è§£èŠç产è åæ¶èŽ¹è ïŒèªç¶èå |
| å»é | Arc<Mutex<HashSet>> | ç®åå¯é ïŒç¬è«åºæ¯åå€è¯»å° |
| è¶ æ¶ | tokio::time::timeout | 鲿¢åäžªæ ¢è¯·æ±æå®æŽäœ |
| äŒé å ³é | Notify + ctrl_c | Ctrl+C æ¶å®æè¿è¡äžç请æ±ïŒäžäž¢å€±æ°æ® |
| 深床éå¶ | max_depth åæ° | 鲿¢æ ééåœç¬å |
â ç产级æ¹è¿æ¹åïŒ
ð¯ è®°äœ Rust å¹¶åçæ žå¿äŒå¿ïŒçŒè¯åšåž®äœ æ£æ¥å¹¶åå®å
šãSend/Sync åšçŒè¯æé»æ¢æ°æ®ç«äºïŒMutex<T> æéåæ°æ®ç»å®ïŒArc ä¿è¯çº¿çšå®å
šçå
±äº«ãäœ äžå¿
äŸèµä»£ç å®¡æ¥æ¥åç°å¹¶å bugââçŒè¯åšå°±æ¯äœ çå¹¶å审æ¥åã