Tokio术语
来自:https://tokio.rs/tokio/glossary
异步(Asynchronous)
在Rust的上下文中,异步代码指的是使用async/await语言特性的代码,它允许许多任务在几个线程(甚至是一个单线程)上并发运行。
并发(Concurrency)和并行(Parallelism)
并发和并行是两个相关的概念,在谈论同时执行多个任务时都会用到。如果某件事情是平行发生的,那么它也是并发发生的,但反过来就不是这样了。在两个任务之间交替进行,但实际上从未同时进行两个任务,这就是并发,但不是并行。
未来(Future)
Future是一个存储某些操作的当前状态的值。future也有一个poll
方法,它使操作继续进行,直到它需要等待某些东西,比如网络连接。对poll
方法的调用应该很快返回。
Future通常是通过在一个异步块中使用.await
来组合多个Future来创建的。
执行器(Executor)/调度器(scheduler)
执行器或调度器是通过重复调用poll
方法来执行future的东西。标准库中没有执行器,所以你需要一个外部库来实现,而使用最广泛的执行器是由Tokio运行时提供。
执行器能够在几个线程上并发地运行大量的 future。它通过在等待时交换当前运行的任务来做到这一点。如果代码花了很长时间都没有到达 .await
,这就被称为 “阻塞线程” 或 “not yielding back to the executor”,这将阻止其他任务的运行。
运行时(Runtime)
运行时是一个库,它包含执行器以及与该执行器集成的各种实用工具,如定时实用工具和IO。运行时和执行器这两个词有时可以互换使用。标准库没有运行时,所以你需要一个外部库来实现,最广泛使用的运行时是 Tokio 运行时。
Runtime这个词也用在其他场合,例如,“Rust没有运行时 “这句话有时被用来表示Rust不执行垃圾收集或即时(just-in-time,JIT)编译。
任务(Task)
任务是在Tokio运行时上运行的操作,由 tokio::spawn
或 Runtime::block_on
函数创建。通过组合创建期货的工具,如 .await
和 join!
并不创建新的任务,每个组合的部分被说成是 “在同一个任务中”。
多个任务是需要并行的,但使用 join!
等工具可以在一个任务上并发地做多件事情。
spawn
spawn 是指使用 tokio::spawn
函数来创建一个新的任务。它也可以指用std::thread::spoon创建新的线程。
异步块(Async block)
异步块是创建一个运行一些代码的future的简单方法。比如说:
let world = async {
println!(" world!");
};
let my_future = async {
print!("Hello ");
world.await;
};
上面的代码创建了一个名为 my_future
的future,如果执行它,就会打印出 Hello world!
。 它是通过首先打印 hello,然后运行 world
future来实现的。请注意,上面的代码不会自己打印任何东西–你必须在任何事情发生之前实际执行my_future,要么直接生成(spawn)它,要么在你生成(spawn)的东西中等待它。
异步函数(Async function)
与异步块类似,异步函数是一种创建函数的简单方法,其主体成为一个future。所有的异步函数都可以被改写成返回一个future的普通函数。
async fn do_stuff(i: i32) -> String {
// do stuff
format!("The integer is {}.", i)
}
use std::future::Future;
// the async function above is the same as this:
fn do_stuff(i: i32) -> impl Future<Output = String> {
async move {
// do stuff
format!("The integer is {}.", i)
}
}
这使用 impl Trait
语法来返回一个 future,因为 Future
是一个 trait。请注意,由于由异步块创建的 future 在执行之前不会做任何事情,所以调用异步函数在其返回的 future 被执行之前不会做任何事情(忽略它将触发一个警告)。
让出(Yielding)
在异步Rust的背景下,Yielding是允许执行者在单个线程上执行许多future的原因。每当一个future让出时,执行者能够将该future与其他future交换,通过反复交换当前任务,执行者可以并发地执行大量的任务。future只能在 .await
时让出,所以在 .await
之间花很长时间的future可以阻止其他任务的运行。
具体来说,future只要从 poll
方法中返回就会让出。
阻塞(Blocking)
“阻塞(blocking)“这个词有两种不同的用法: 阻塞的第一个含义是简单地等待某事完成,而阻塞的另一个含义是当一个 future 花很长的时间而不让出。为了明确起见,你可以用 “阻塞线程” 这个短语来表示第二种含义。
Tokio的文档总是使用 “阻塞” 的第二种含义。
要在Tokio中运行阻塞代码,请参见Tokio API参考中的 CPU绑定任务和阻塞代码 部分。
流(Stream)
Stream
是 Iterator
的异步版本,它提供了一个数值流。它通常与 while let
循环一起使用,就像这样:
use tokio_stream::StreamExt; // for next()
while let Some(item) = stream.next().await {
// do something
}
流这个词有时被混乱地用来指代 AsyncRead
和 AsyncWrite
特性。
Tokio的流工具目前是由 tokio-stream
crate提供的。一旦Stream特性在std中稳定下来,Stream工具将被移到 tokio crate中。
通道(Channel)
通道是一种工具,允许代码的一个部分向其他部分发送消息。Tokio提供了许多通道,每个通道都有不同的用途:
- mpsc:多生产者、单消费者通道。可以发送许多值。
- oneshot:单生产者,单消费者通道。可以发送一个单一的值。
- broadcast:多生产者,多消费者。可以发送许多值。每个接收者看到每个值。
- watch:单生产者,多消费者。可以发送许多值,但不保留历史。接收者只看到最新的值。
如果你需要一个多生产者多消费者的通道,每个消息只有一个消费者看到,你可以使用 async-channel
crate。
还有一些通道是在异步Rust之外使用的,比如 std::sync::mpsc
和 crossbeam::channel
。这些通道通过阻塞线程来等待消息,这在异步代码中是不允许的。
背压(Backpressure)
背压是一种设计对高负荷反应良好的应用程序的模式。例如,mpsc
通道有有界的和无界的两种形式。通过使用有界通道,如果接收方不能跟上消息的数量,接收方可以对发送方施加 “背压”,这就避免了随着通道上的消息越来越多,内存使用量无限制地增长。
Actor
一种设计应用程序的模式。Actor是指一个独立生成的任务,它代表应用程序的其他部分管理一些资源,使用通道与应用程序的其他部分进行通信。