Rust:tokio 可以理解为类似于 Javascripts 事件循环还是可以像它一样使用?
Rust: can tokio be understood as similar to Javascripts event loop or be used like it?
我不确定 tokio
是否类似于 Javascript 中的事件循环,也是一个非阻塞运行时,或者它是否可以用于以类似的方式工作。在我的理解中,tokio
是 Rust 中未来的运行时。因此它必须实现某种用户态线程或任务,这可以通过事件循环(至少部分)来实现以安排新任务。
让我们采用以下 Javascript 代码:
console.log('hello1');
setTimeout(() => console.log('hello2'), 0);
console.log('hello3');
setTimeout(() => console.log('hello4'), 0);
console.log('hello5');
输出将是
hello1
hello3
hello5
hello2
hello4
我如何在东京做到这一点? tokio 是否打算像这样整体工作?我尝试了以下代码
async fn set_timeout(f: impl Fn(), ms: u64) {
tokio::time::sleep(tokio::time::Duration::from_millis(ms)).await;
f()
}
#[tokio::main]
async fn main() {
println!("hello1");
tokio::spawn(async {set_timeout(|| println!("hello2"), 0)}).await;
println!("hello3");
tokio::spawn(async {set_timeout(|| println!("hello4"), 0)}).await;
println!("hello5");
}
输出只是
hello1
hello3
hello5
如果我将代码更改为
println!("hello1");
tokio::spawn(async {set_timeout(|| println!("hello2"), 0)}.await).await;
println!("hello3");
tokio::spawn(async {set_timeout(|| println!("hello4"), 0)}.await).await;
println!("hello5");
输出为
hello1
hello2
hello3
hello4
hello5
但后来我不明白整个 async/await/future 功能的要点,因为那时我的“异步”set_timeout-tasks 实际上阻止了其他 println 语句..
与 JavaScript 不同,Rust 在等待未来之前不会开始执行异步函数。这意味着set_timeout(|| println!("hello2"), 0)
只会创造一个新的未来。它根本不执行它。当你等待它时,它才会被执行。 .await
本质上是阻塞当前线程,直到未来完成,这不是“真正的异步应用程序”。要让你的代码像 JavaScript 一样并发,你可以使用 join!
宏:-
use tokio::join;
use tokio::time::*;
async fn set_timeout(f: impl Fn(), ms: u64) {
sleep(Duration::from_millis(ms)).await;
f()
}
#[tokio::main]
async fn main() {
println!("hello1");
let fut_1 = tokio::spawn(set_timeout(|| println!("hello2"), 0));
println!("hello3");
let fut_2 = tokio::spawn(set_timeout(|| println!("hello4"), 0));
println!("hello5");
join!(fut_1, fut_2);
}
想感受一下Promise.all
可以用FuturesOrdered。
更多信息:-
简而言之:是的,Tokio 的工作方式很像 JavaScript 事件循环。但是,您的第一个代码段存在三个问题。
首先,它 return 来自 main()
,然后才等待事情结束。与您的 JavaScript 代码不同,它可能在浏览器中 运行s,并且 运行s 即使在您在控制台中键入的代码完成 运行ning 之后也会超时,Rust代码在一个短暂的可执行文件中,在 main()
之后终止。如果可执行文件停止 运行ning,则计划稍后发生的任何事情都不会发生,因为它 return 从 main()
.
编辑而来
第二个问题是调用 set_timeout()
异步函数的匿名异步块不会对其 return 值执行任何操作。 Rust 和 JavaScript 中的异步函数之间的一个重要区别是,在 Rust 中你不能只 调用 一个异步函数然后用它完成。在 JavaScript 中,异步函数 return 是一个承诺,如果您不等待该承诺,事件循环仍将在后台执行异步函数的代码。在 Rust 中,异步函数 return 是未来,但它不与任何事件循环关联,它只是为某人 运行 准备的。然后,您需要使用 .await
等待它(与 JavaScript 中的含义相同)或显式将其传递给 tokio::spawn()
以在后台执行(与调用的含义相同但不等待 JavaScript 中的函数)。您的异步块两者都不做,因此 set_timeout()
的调用是空操作。
最后,代码立即等待 spawn()
创建的任务,这首先违背了调用 spawn()
的目的 - tokio::spawn(foo()).await
在功能上等同于 foo().await
对于任何 foo()
.
第一个问题可以通过在 main
末尾添加一个小睡眠来解决。 (这不是正确的修复,但可以用来演示发生了什么。)第二个问题可以通过删除异步块并将 set_timeout()
的 return 值传递给 tokio::spawn()
来解决.第三个问题通过删除不必要的 .await
任务得到解决。
#[tokio::main]
async fn main() {
println!("hello1");
tokio::spawn(set_timeout(|| println!("hello2"), 0));
println!("hello3");
tokio::spawn(set_timeout(|| println!("hello4"), 0));
println!("hello5");
tokio::time::sleep(tokio::time::Duration::from_millis(1)).await;
}
此代码将打印“预期的”1、3、5、4、2(尽管在此类程序中不能保证顺序)。真正的代码不会以 sleep
结尾;相反,它会等待它创建的任务,如 Shivam 的回答所示。
我不确定 tokio
是否类似于 Javascript 中的事件循环,也是一个非阻塞运行时,或者它是否可以用于以类似的方式工作。在我的理解中,tokio
是 Rust 中未来的运行时。因此它必须实现某种用户态线程或任务,这可以通过事件循环(至少部分)来实现以安排新任务。
让我们采用以下 Javascript 代码:
console.log('hello1');
setTimeout(() => console.log('hello2'), 0);
console.log('hello3');
setTimeout(() => console.log('hello4'), 0);
console.log('hello5');
输出将是
hello1
hello3
hello5
hello2
hello4
我如何在东京做到这一点? tokio 是否打算像这样整体工作?我尝试了以下代码
async fn set_timeout(f: impl Fn(), ms: u64) {
tokio::time::sleep(tokio::time::Duration::from_millis(ms)).await;
f()
}
#[tokio::main]
async fn main() {
println!("hello1");
tokio::spawn(async {set_timeout(|| println!("hello2"), 0)}).await;
println!("hello3");
tokio::spawn(async {set_timeout(|| println!("hello4"), 0)}).await;
println!("hello5");
}
输出只是
hello1
hello3
hello5
如果我将代码更改为
println!("hello1");
tokio::spawn(async {set_timeout(|| println!("hello2"), 0)}.await).await;
println!("hello3");
tokio::spawn(async {set_timeout(|| println!("hello4"), 0)}.await).await;
println!("hello5");
输出为
hello1
hello2
hello3
hello4
hello5
但后来我不明白整个 async/await/future 功能的要点,因为那时我的“异步”set_timeout-tasks 实际上阻止了其他 println 语句..
与 JavaScript 不同,Rust 在等待未来之前不会开始执行异步函数。这意味着set_timeout(|| println!("hello2"), 0)
只会创造一个新的未来。它根本不执行它。当你等待它时,它才会被执行。 .await
本质上是阻塞当前线程,直到未来完成,这不是“真正的异步应用程序”。要让你的代码像 JavaScript 一样并发,你可以使用 join!
宏:-
use tokio::join;
use tokio::time::*;
async fn set_timeout(f: impl Fn(), ms: u64) {
sleep(Duration::from_millis(ms)).await;
f()
}
#[tokio::main]
async fn main() {
println!("hello1");
let fut_1 = tokio::spawn(set_timeout(|| println!("hello2"), 0));
println!("hello3");
let fut_2 = tokio::spawn(set_timeout(|| println!("hello4"), 0));
println!("hello5");
join!(fut_1, fut_2);
}
想感受一下Promise.all
可以用FuturesOrdered。
更多信息:-
简而言之:是的,Tokio 的工作方式很像 JavaScript 事件循环。但是,您的第一个代码段存在三个问题。
首先,它 return 来自 main()
,然后才等待事情结束。与您的 JavaScript 代码不同,它可能在浏览器中 运行s,并且 运行s 即使在您在控制台中键入的代码完成 运行ning 之后也会超时,Rust代码在一个短暂的可执行文件中,在 main()
之后终止。如果可执行文件停止 运行ning,则计划稍后发生的任何事情都不会发生,因为它 return 从 main()
.
第二个问题是调用 set_timeout()
异步函数的匿名异步块不会对其 return 值执行任何操作。 Rust 和 JavaScript 中的异步函数之间的一个重要区别是,在 Rust 中你不能只 调用 一个异步函数然后用它完成。在 JavaScript 中,异步函数 return 是一个承诺,如果您不等待该承诺,事件循环仍将在后台执行异步函数的代码。在 Rust 中,异步函数 return 是未来,但它不与任何事件循环关联,它只是为某人 运行 准备的。然后,您需要使用 .await
等待它(与 JavaScript 中的含义相同)或显式将其传递给 tokio::spawn()
以在后台执行(与调用的含义相同但不等待 JavaScript 中的函数)。您的异步块两者都不做,因此 set_timeout()
的调用是空操作。
最后,代码立即等待 spawn()
创建的任务,这首先违背了调用 spawn()
的目的 - tokio::spawn(foo()).await
在功能上等同于 foo().await
对于任何 foo()
.
第一个问题可以通过在 main
末尾添加一个小睡眠来解决。 (这不是正确的修复,但可以用来演示发生了什么。)第二个问题可以通过删除异步块并将 set_timeout()
的 return 值传递给 tokio::spawn()
来解决.第三个问题通过删除不必要的 .await
任务得到解决。
#[tokio::main]
async fn main() {
println!("hello1");
tokio::spawn(set_timeout(|| println!("hello2"), 0));
println!("hello3");
tokio::spawn(set_timeout(|| println!("hello4"), 0));
println!("hello5");
tokio::time::sleep(tokio::time::Duration::from_millis(1)).await;
}
此代码将打印“预期的”1、3、5、4、2(尽管在此类程序中不能保证顺序)。真正的代码不会以 sleep
结尾;相反,它会等待它创建的任务,如 Shivam 的回答所示。