rust - Rust:tokio 可以理解为类似于 Javascripts 的事件循环,或者像它一样使用吗?
问题描述
我不确定它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 语句。
解决方案
简而言之:是的,Tokio 旨在像 JavaScript 事件循环一样工作。但是,您的第一个代码段存在三个问题。
首先,它main()
在等待事情结束之前返回。与你的 JavaScript 代码不同,它可能在浏览器中运行,并且即使在你在控制台中键入的代码完成运行后也会运行超时,Rust 代码是一个短暂的可执行文件,它在main()
. 如果可执行文件因为从main()
.
第二个问题是调用set_timeout()
异步函数的匿名异步块不对其返回值做任何事情。Rust 和 JavaScript 中的异步函数之间的一个重要区别是,在 Rust 中,你不能只调用一个异步函数并完成它。在 JavaScript 中,异步函数返回一个 Promise,如果您不等待该 Promise,事件循环仍将在后台执行异步函数的代码。在 Rust 中,异步函数返回一个未来,但它不与任何事件循环相关联,它只是为有人运行它做准备。然后,您需要使用.await
(与 JavaScript 中的含义相同)等待它或将其显式传递给tokio::spawn()
在后台执行(与在 JavaScript 中调用但不等待函数的含义相同)。您的 async 块两者都不做,因此调用 ofset_timeout()
是无操作的。
最后,代码立即等待由 创建的任务spawn()
,这违背了spawn()
首先调用的目的——tokio::spawn(foo()).await
在功能上等同于foo().await
for any foo()
。
第一个问题可以通过在main
. (这不是正确的修复,但将用于演示发生了什么。)第二个问题可以通过删除 async 块并仅传递 to 的返回值来set_timeout()
修复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 的回答所示。
推荐阅读
- wordpress - Woocommerce 支付网关很奇怪,谢谢重定向
- python - 错误:列表索引必须是整数或切片,而不是元组
- javascript - 为什么我打开网站时闹钟响了?
- javascript - 为什么在 react-data-table 组件的第一列中自动添加行数
- macos - 连接到 Apple Sandbox 以进行收据验证时出现 SSL 错误
- powershell - 在 Powershell 中更改 AD 属性时出错
- c# - 使用 c# Visual Studio 在文本框中避免空格并显示错误消息
- python - 通过链接而不是复制来列出连接
- javascript - 如何将 document.referrer 与两种不同的模式一起使用?
- sql - 如果其他列与某个 DataType 匹配,如何将行更新为有效?