当前位置: 首页 > 知识库问答 >
问题:

Rust:tokio可以被理解为类似于Javascripts事件循环还是像它一样使用?

阎冠玉
2023-03-14

我不确定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中执行此操作?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/wait/Future功能的重点,因为我的“async”set_timeout任务实际上是阻塞了其他println语句...

共有2个答案

林夕
2023-03-14

与JavaScript不同,Rust不会启动异步函数的执行,直到等待未来。意思是< code>set_timeout(|| println!(“hello2”),0)只创造新的未来。它根本不执行它。当你等待它的时候,它才会被执行。<代码>。await实质上阻塞了当前线程,直到未来线程完成,这不是“真正的异步应用程序”。要让你的代码像JavaScript一样并发,可以使用< code>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);
}

如果您想了解< code>Promise.all,可以使用FuturesOrdered。

更多信息:-

  • https://news.ycombinator.com/item?id=21473777
  • https://rust-lang.github.io/async-book/06_multiple_futures/01_chapter.html
汪正卿
2023-03-14

简而言之:是的,Tokio的工作方式与JavaScript事件循环非常相似。但是,您的第一个片段有三个问题。

首先,它在等待事情发生之前从main()返回。与 JavaScript 代码不同,JavaScript 代码可能在浏览器中运行,即使在您在控制台中键入的代码完成运行后也会运行超时,Rust 代码位于一个短暂的可执行文件中,该可执行文件main() 之后终止。如果可执行文件停止运行,则计划稍后发生的任何事情都不会发生,因为它从main()返回。

第二个问题是调用set_timeout()异步函数的匿名异步块对其返回值没有任何作用。Rust和JavaScript中的异步函数之间的一个重要区别是,在Rust中,您不能只调用异步函数就完成了它。在JavaScript中,异步函数返回一个promise,如果您不等待该promise,事件循环仍将在后台执行异步函数的代码。在Rust中,异步函数返回一个future,但它与任何事件循环都没有关联,它只是为运行它做准备。然后,您需要使用等待它。等待(与JavaScript中的含义相同)或显式地将其传递给tokio::spawn()以在后台执行(与调用相同,但不等待JavaScript中的函数)。异步块两者都不做,因此调用<code>set_timeout()</code>是不可行的。

最后,代码立即等待 spawn() 创建的任务,这首先违背了调用 spawn() 的目的 - tokio::spawn(foo()).await 在功能上等同于 foo().await for any foo())。

第一个问题可以通过在main的末尾添加一个小睡眠来解决。(这不是正确的修复,但可以用来演示发生了什么。)第二个问题可以通过删除异步块并将<code>set_timeout()来解决。第三个问题通过删除不必要的来解决。等待任务的

#[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(尽管在此类程序中不能保证顺序)。真正的代码不会以睡眠结束;相反,它将等待它创建的任务,如Shivam的答案所示。

 类似资料:
  • 本文向大家介绍理解JS事件循环,包括了理解JS事件循环的使用技巧和注意事项,需要的朋友参考一下 伴随着JavaScript这种web浏览器脚本语言的普及,对它的事件驱动交互模型,以及它与Ruby、Python和Java中常见的请求-响应模型的区别有一个基本了解,对您是有益的。在这篇文章中,我将解释一些JavaScript并发模型的核心概念,包括其事件循环和消息队列,希望能够提升你对一种语言的理解,

  • 问题内容: 我正在考虑,这是我想到的: 假设我们有这样的代码: 一个请求进入,并且JS引擎逐步开始执行上述代码。前两个呼叫是同步呼叫。但是当涉及到方法时,它将成为异步执行。但是JS立即从中返回并继续执行,这称为或。并且它继续在其他等等上工作。 执行结果如下: 交流数据库 因此,基本上第二个首先完成,其回调函数比第一个早执行,这是有道理的。 我们在这里谈论单线程应用程序。JS Engine会继续执行

  • 本文向大家介绍深入理解JQuery循环绑定事件,包括了深入理解JQuery循环绑定事件的使用技巧和注意事项,需要的朋友参考一下 深入理解JQuery循环绑定事件 以上这篇深入理解JQuery循环绑定事件就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持呐喊教程。

  • 本文向大家介绍什么是事件循环?相关面试题,主要包含被问及什么是事件循环?时的应答技巧和注意事项,需要的朋友参考一下 Node采用的是单线程的处理机制(所有的I/O请求都采用非阻塞的工作方式),至少从Node.js开发者的角度是这样的。而在底层,Node.js借助libuv来作为抽象封装层,从而屏蔽不同操作系统的差异,Node可以借助livuv来实现线程。下图表示Node和libuv的关系 Libu

  • 声明函数以映射值并在存在值时使用它们是很方便的。 在您有几个强制对象和几个可选对象的情况下,我发现自己将其他对象包装为可选对象。也可以使用(mandatoryObject),这样我就可以在它们上使用相同的表达式,而不必全部倒写。 但是我不喜欢这个代码: 所以我把它包装起来: 但这是错误的,因为蔬菜(在这个例子中)实际上不是可选的。它们非常重要,我只是给每个人的印象是它们是可选的。 所以我的问题是:

  • 事件是应用程序与自身各个功能模块以及与操作系统进行通讯的手段,也是实现事件驱动编程模型的基础,应用程序如果要响应这些事件,通常是创建一个事件队列来集中存放它们,从事件队列取出事件并调用对应处理器就是一次事件响应,而往复执行这个操作的过程就是事件循环。 驱动接口 LCUI 对事件循环的操作有处理事件、绑定事件和解绑事件,驱动模块的职责就是基于操作系统接口向 LCUI 提供实现了这些操作的接口。首先我