rust - 意外的 tokio::task::spawn_blocking 行为
问题描述
我正在试验 tokio tokio::spawn
,tokio::task::spawn
结果我不明白后者的行为。
当我运行以下代码时:
#[tokio::main]
pub async fn main() {
// I'm spawning one block of functions
let h = tokio::task::spawn_blocking(move || {
block_one();
});
// and another block of functions
let h2 = tokio::spawn(async move {
block_two().await;
});
// then I collect the handles
h.await.unwrap();
h2.await.unwrap();
}
#[tokio::main] //needed as this block is not treated as syncronous by main
pub async fn block_one() {
let mut handles = vec![];
for i in 1..10 {
let h = tokio::spawn(async move {
println!("Starting func #{}", i);
i_take_random_time().await;
println!("Ending func #{}", i);
});
handles.push(h);
}
for h in handles {
h.await.unwrap();
}
}
pub async fn block_two() {
let mut handles = vec![];
for i in 10001..10010 {
let h = tokio::spawn(async move {
println!("Starting func #{}", i);
i_take_random_time().await;
println!("Ending func #{}", i);
});
handles.push(h);
}
for h in handles {
h.await.unwrap();
}
}
我的期望是第一个功能块将完全运行 - 只有第二个块才能运行。这就是我对“spawn_blocking”的理解——它会阻止进一步的执行,直到其中的任何内容都完成为止。
我实际上得到的是第二个函数块首先开始(全部,全部 10 个) - 只有在第一个块开始时。所以这与我的预期完全相反。
更令人困惑的是,当我修改上面的代码以spawn_blocking
使两个块都具有 - 所有 20 个函数一起开始时,就好像两个块都是一个大异步循环的一部分。再次不是我所期望的 - 我认为第一个块会运行,在它完成之前阻塞,然后第二个会运行。
有人可以帮我破译发生了什么吗?
此 repo中提供了重现上述 2 个场景的完整代码。
- 场景 5 = 我描述的第一个案例
- 场景 6 = 我描述的第二种情况
注意:这里有两个级别的异步:BETWEEN 块和 WITHIN 块。希望有助于避免任何混乱。
解决方案
听起来您希望spawn_blocking
阻止其他事物运行,但其目的恰恰相反。的目的spawn_blocking
是为了避免阻塞其他东西运行。
主要spawn_blocking
用于那些由于使用非异步操作(例如std::net
. 它通过将它们卸载到单独的线程池来实现这一点。该名称来自您正在生成阻塞操作以便它可以在其他地方运行的事实。
要等待第一个块完成,您可以这样做:
#[tokio::main]
pub async fn main() {
// I'm spawning one block of functions
let h = tokio::task::spawn_blocking(move || {
block_one();
});
// wait for the first block
h.await.unwrap();
// then spawn another block of functions
let h2 = tokio::spawn(async move {
block_two().await;
});
h2.await.unwrap();
}
请注意,在内部立即使用#[tokio::main]
(或)很少是您想要的。只需使用 . 生成一个普通任务。block_on
spawn_blocking
tokio::spawn
推荐阅读
- sql - 拉取 Min_date 和 Max date 的列值(min_date 24 个月内)
- scala - 我们如何在 Flink CEP 中检测到持续一段时间的模式?
- java - OpenTok 服务器设置
- java - 将 Java 派生的 Thread 类添加到 Vector 失败
- reactjs - 使用保持某些状态的钩子时如何使组件重新渲染
- prometheus - 当所有抓取作业都关闭时,Prometheus 中的主机关闭警报
- xml - 如何从具有命名空间的 XML 中获取节点
- c# - c# 多维数组迭代
- batch-file - 如何在 Windows 10 上运行带有批处理文件的 ipython notebook?
- scala - 理解抽象方法覆盖具体方法时的逻辑