作者:京东科技 贾世闻
Tokio 无疑是 Rust 世界中最优良的异步 Runtime 实现。非阻塞的个性带来了优异的性能,然而在理论的开发中咱们往往须要在某些状况下阻塞工作来实现某些性能。
咱们看看上面的例子
fn main(){
let max_task = 1;
let rt = runtime::Builder::new_multi_thread()
.worker_threads(max_task)
.build()
.unwrap();
rt.block_on(async {println!("tokio_multi_thread");
for i in 0..100 {println!("run {}", i);
tokio::spawn(async move {println!("spawn {}", i);
thread::sleep(Duration::from_secs(2));
});
}
});
}
咱们期待的运行构造是通过异步工作打印出 99 个“spawn i”,但理论输入的后果大略这样
tokio_multi_thread
run 0
run 1
run 2
.......
run 16
spawn 0
run 17
......
run 99
spawn 1
spawn 2
......
spawn 29
......
spawn 58
spawn 59
59 执行完前面就没有输入了,如果把 max_task 设置为 2,状况会好一点,然而也没有执行完所有的异步操作,也就是说在资源有余的状况下,Tokio 会摈弃某些工作,这不合乎咱们的预期。那么能不能再达到了某一阀值的状况下阻塞一下,不再给 Tokio 新的工作呢。这有点相似线程池,当达达最大线程数的时候阻塞前面的工作待有开释的线程后再持续。
咱们看看上面的代码。
fn main(){
let max_task = 2;
let rt = runtime::Builder::new_multi_thread()
.worker_threads(max_task)
.enable_time()
.build()
.unwrap();
let mut set = JoinSet::new();
rt.block_on(async {
for i in 0..100 {println!("run {}", i);
while set.len() >= max_task {set.join_next().await;
}
set.spawn(async move {sleep().await;
println!("spawn {}", i);
});
}
while set.len() > 0 {set.join_next().await;
}
});
}
咱们应用 JoinSet 来治理派生进去的工作。set.join_next().await; 保障至多一个工作被执行实现。联合 set 的 len,咱们能够在工作达到下限时阻塞工作派生。当循环完结,可能还有未实现的工作,所以只有 set.len() 大于 0 就期待工作完结。
输入大略长这样
running 1 test
tokio_multi_thread
run 0
run 1
spawn 0
run 2
spawn 1
......
run 31
spawn 30
run 32
spawn 31
run 33
......
run 96
spawn 95
run 97
spawn 96
run 98
spawn 97
run 99
spawn 98
spawn 99
合乎预期,代码不多,有趣味的同学能够入手尝试一下。