Hello Tokio

Posted 跨链技术践行者

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hello Tokio相关的知识,希望对你有一定的参考价值。

Tokio教程之Hello Tokio

https://tokio.rs/tokio/tutorial/hello-tokio

我们将通过编写一个非常基本的Tokio应用程序开始。它将连接到Mini-Redis服务器,将密钥hello的值设置为world。然后它将读回密钥。这将使用Mini-Redis客户端库来完成。

代码

use mini_redis::{client, Result};

#[tokio::main]
pub async fn main() -> Result<()> {
    // Open a connection to the mini-redis address.
    let mut client = client::connect("127.0.0.1:6379").await?;

    // Set the key "hello" with value "world"
    client.set("hello", "world".into()).await?;

    // Get key "hello"
    let result = client.get("hello").await?;

    println!("got value from the server; result={:?}", result);

    Ok(())
}

Copy

代码分解

让我们花点时间来看看我们刚刚做了什么。没有太多的代码,但有很多事情正在发生。

let mut client = client::connect("127.0.0.1:6379").await?;

Copy

client::connect 函数是由 mini-redis crate提供的。它异步地与指定的远程地址建立了一个TCP连接。一旦连接建立起来,就会返回一个 client 句柄。尽管操作是异步进行的,但我们写的代码看起来是同步的。唯一表明该操作是异步的是 .await 操作符。

什么是异步编程?

大多数计算机程序的执行顺序与它的编写顺序相同。第一行执行,然后是下一行,以此类推。在同步编程中,当程序遇到不能立即完成的操作时,它就会阻塞,直到操作完成。例如,建立一个TCP连接需要在网络上与一个对等体进行交换,这可能需要相当长的时间。在这段时间内,线程会被阻塞。

通过异步编程,不能立即完成的操作被暂停到后台。线程没有被阻塞,可以继续运行其他事情。一旦操作完成,任务就会被取消暂停,并继续从它离开的地方处理。我们之前的例子中只有一个任务,所以在它被暂停的时候什么都没有发生,但异步程序通常有许多这样的任务。

尽管异步编程可以带来更快的应用,但它往往导致更复杂的程序。程序员需要跟踪所有必要的状态,以便在异步操作完成后恢复工作。从历史上看,这是一项繁琐且容易出错的任务。

编译时绿色线程

Rust使用一个叫做 async/await 的功能实现了异步编程。执行异步操作的函数都标有 async 关键字。在我们的例子中,connect函数是这样定义的:

use mini_redis::Result;
use mini_redis::client::Client;
use tokio::net::ToSocketAddrs;

pub async fn connect<T: ToSocketAddrs>(addr: T) -> Result<Client> {
    // ...
}

Copy

async fn 的定义看起来像一个普通的同步函数,但却以异步方式运行。Rust在编译时将 async fn 转化为一个异步运行的routine。在 async fn 中对 .await 的任何调用都会将控制权交还给线程。当操作在后台进行时,线程可以做其他工作。

尽管其他语言也实现了async/await,但Rust采取了一种独特的方法。主要是,Rust的异步操作是 lazy 的。这导致了与其他语言不同的运行时语义。

如果这还不是很有意义,不要担心。我们将在本指南中更多地探讨async/await。

使用 async/await

异步函数的调用与其他Rust函数一样。然而,调用这些函数并不会导致函数主体的执行。相反,调用 async fn 会返回一个代表操作的值。这在概念上类似于一个零参数闭包。要实际运行该操作,你应该在返回值上使用 .await 操作符。

例如,给定的程序:

async fn say_world() {
    println!("world");
}

#[tokio::main]
async fn main() {
    // Calling `say_world()` does not execute the body of `say_world()`.
    let op = say_world();

    // This println! comes first
    println!("hello");

    // Calling `.await` on `op` starts executing `say_world`.
    op.await;
}

Copy

输出为:

hello
world

async fn 的返回值是一个匿名类型,它实现了 Future trait。

异步main函数

用于启动应用程序的main函数与大多数Rust工具箱中的常见函数不同。

  • 它是 async fn
  • 它被注解为 #[tokio::main] 。

使用 async fn 是因为我们想进入一个异步上下文。然而,异步函数必须由一个运行时来执行。运行时包含异步任务调度器,提供事件化I/O、计时器等。运行时不会自动启动,所以主函数需要启动它。

#[tokio::main] 函数是一个宏。它将 async fn main() 转换为同步 1fn main(),初始化一个运行时实例并执行异步main函数。

例如,下面的例子:

#[tokio::main]
async fn main() {
    println!("hello");
}

Copy

被转化为:

fn main() {
    let mut rt = tokio::runtime::Runtime::new().unwrap();
    rt.block_on(async {
        println!("hello");
    })
}

Copy

Cargo features

在本教程中依赖Tokio时,启用了 full 的功能标志:

tokio = { version = "1", features = ["full"] }

Copy

Tokio 有很多功能(TCP、UDP、Unix 套接字、定时器、同步工具、多种调度器类型等)。不是所有的应用程序都需要所有的功能。当试图优化编译时间或最终应用程序的足迹时,应用程序可以决定只选择进入它所使用的功能。

以上是关于Hello Tokio的主要内容,如果未能解决你的问题,请参考以下文章

Tokio教程之select

Tokio 1.0发布,Rust异步编程框架

Rust网络编程框架-深入理解Tokio中的管道

Rust网络编程框架-深入理解Tokio中的管道

asynchronous - rust lazy_static和tokio::select中的tokio::sync::mpsc::channel

Rust网络编程框架-Tokio进阶