tokio的开头引入了一个task的概念
task可以理解为异步执行的函数或者闭包
它可以在等待某些操作(如I/O操作、定时器等)完成时释放控制权,允许其他任务继续执行。这种机制使得Rust能够以非阻塞的方式同时处理多个任务,从而提高程序的并发性和效率。
async fn my_task() {
// 异步代码块
let result = some_async_operation().await;
println!("Operation result: {}", result);
}
#[tokio::main]
async fn main() {
// 运行异步任务
my_task().await;
}
在这个例子中,my_task是一个异步函数,它使用.await来等待some_async_operation这个异步操作的结果。#[tokio::main]宏用于设置异步运行时环境,使得我们可以在main函数中调用异步的my_task。
在Rust中,当一个task需要等待某些操作,比如I/O或者延迟(使用async-std或tokio中的delay函数),它并不会被完全中断或挂起。相反,Rust的异步运行时会将该task的执行状态保存下来,并将其从当前的执行线程中移除,以便其他任务可以继续执行。
这种机制是通过异步运行时的调度器来实现的,它负责管理和调度所有的异步任务。当一个task遇到一个await点,它会通知运行时,运行时会检查是否有其他已经准备好执行的任务。如果有,运行时会切换到另一个任务,而当前的任务会在相应的future被准备好时再次被调度执行。
开始翻译tokio:
crate tokio
tokio是一个不影响速度的编写可靠程序的运行时
是一个事件驱动的,非阻塞的IO平台,编写异步使用Rust编程语言的程序,主要提供了几个组件
- 用于处理异步任务的工具,包括同步原语和channel以及timeouts超时,sleeps睡眠和intervals
- 执行异步IO的APIs,包括TCP和UDP套接字socket、文件系统操作以及进程和信号管理。
- 一个由操作系统的事件队列(epoll、kqueue、IOCP等)支持的I/O驱动,以及一个高性能定时器。
A tour of tokio
Tokio 由许多模块组成,这些模块提供一系列功能对于在 Rust 中实现异步应用程序至关重要。在这个部分,我们将简要介绍 Tokio,总结主要的 API 和它们的用途。
最简单的入门方法是启用所有功能。为此,请启用功能标志:full
即添加依赖
[dependes]
tokio = { version = "1", features = ["full"] }
Authoring libraries编写库
当你编写一个库的时候需要提供轻量级的crate以确保只需要启用用户需要的功能。
example
tokio = { version = "1", features = ["rt", "net"] }
如果只需要tokio::spawn 并使用 TcpStream,需要以上述的方式导入依赖
以task的方式运行
Rust的异步程序是基于轻量级非阻塞的称之为task的执行单元的,tokio::task模块提供了重要的task运行工具。
1、spawn 函数和 JoinHandle 类型,分别在 Tokio 运行时和等待生成任务的输出的时候调度新任务。
2、在异步任务上下文中运行阻塞操作的函数
tokio::task 模块仅在 “rt” 功能标志时才存在。
tokio::sync模块包含在需要通信或共享数据时使用的同步原语。其中包括:
频道(OneShot、MPSC、Watch 和 Broadcast)用于任务task之间发送值的时候。
非阻塞互斥锁用于控制共享可变的值。
异步 Barrier 类型,以便在开始计算之前同步多个任务。
tokio::sync模块仅在启用“sync”功能标志时才存在。
tokio::time模块提供了跟踪时间和安排工作的实用程序。其中包括设置任务超时、睡眠工作以在未来运行或每隔一段时间重复操作的功能。
为了使用tokio::time,必须启用“time”功能标志。
最后,Tokio提供了一个用于执行异步任务的运行时。大多数应用程序都可以使用#[tokio::main]宏在tokio运行时上运行代码。但是,此宏仅提供基本配置选项。作为替代方案,tokio::runtime 模块提供了更强大的API来配置和管理运行时。如果#[tokio::main]宏不能提供所需的功能,则应该使用该模块。
使用运行时需要“rt”或“rt-multi-thread”功能标志,以分别启用当前线程单线程调度程序和多线程调度程序。有关详细信息,请参阅运行时模块文档。此外,“宏”功能标志启用#[tokio::main]和#[tokio::test]属性。
cpu-bound task and blocking code
(cpu密集型任务,对应IO密集型任务,一个需要多次的输入输出,一个需要长期占用cpu进行计算
Tokio能够通过重复交换每个线程上当前运行的任务,在几个线程上同时运行许多任务。然而,这种交换只能发生在.await点,因此长时间未到达.await的代码将阻止其他任务运行。为了解决这个问题,Tokio提供了两种线程:核心线程和阻塞线程。(一个代码在.await之前会长期占用cpu,await实现的线程称之为协程,轻量级线程)
核心线程是所有异步代码运行的地方,默认情况下,Tokio将为每个CPU核心生成一个线程。可以使用环境变量TOKIO_WORKER_THREADS来覆盖默认值。
阻塞线程是按需生成的,可用于运行阻塞代码,否则会阻止其他任务运行,并且在一定时间内不使用时保持活动状态,可以使用thread_keep_alive进行配置。由于Tokio不可能像使用异步代码那样交换阻塞任务,因此阻塞线程数的上限非常大。这些限制可以在Builder上配置。
要生成阻塞任务,您应该使用spawn_blocking函数。
(阻塞线程用于执行一些阻塞性的操作,提高并发性)
#[tokio::main]
async fn main() {
// 运行在一个核心线程(系统线程)
let blocking_task = tokio::task::spawn_blocking(|| {
// 这运行在一个阻塞线程
// 在这里阻塞是ok的
});
// 我们可以这样等待阻塞任务:
// 如果阻塞任务恐慌panic,下面的unwrap将传播恐慌
blocking_task.await.unwrap();
}
Rayon是一个数据并行库,可以轻松地将顺序计算转换为并行计算。
它重量轻,便于在现有代码中引入并行性。它保证了无数据竞争的执行,并在合理的情况下根据运行时的工作负载利用并行性。
如果你的代码是cpu密集型的代码并且你希望限制运行的线程数量,那么你可以用一个线程池。比如说你可以考虑用一个rayon库来处理cpu密集型任务,但是如果你这样做了你就要小心这个额外的运行时只能运行cpu密集任务,如果运行了IO密集型任务就会表现不佳
思考天地
我们知道这个事情,cpu的每个核心会默认配置一个core thread来执行任务。
如果4核,队列里8件事就会轮流执行这8件事情。
如果这8件事情前四件是IO密集,在IO结束之前无法到达await,整个process就会进入阻塞状态,造成极大的cpu资源浪费,那么我只能选择把他放在阻塞线程,进行IO时就会进行上下文切换,注意到这个切换的代价相较于task遇到await的切换是大很多的,task遇到await的上下文切换是轻量级的
IO阻塞的问题由于无法遇到await所以无法借用task的轻量级切换
哈哈哈哈哈哥们真nb啊
异步IO
补充:关于await?
await用于等待future所返回的值,如果future的返回值产生一个错误则需要显示的进行处理,用result或者if let进行处理,如果使用?就可以简化这个过程直接把错误传递出去,总而终止当前的异步函数
举个例子
async fn fetch_data() -> Result<String, reqwest::Error> {
let response = reqwest::get("https://example.com/data")
.await?; // 如果请求失败,这里会返回一个错误
let text = response.text().await?; // 如果读取响应体失败,这里也会返回一个错误
Ok(text) // 如果一切顺利,返回获取到的数据
}
#[tokio::main]
async fn main() {
match fetch_data().await {
Ok(data) => println!("Fetched data: {}", data),
Err(e) => eprintln!("Failed to fetch data: {}", e),
}
}
使用?可以把问题放在主函数里去处理,直接返回一个错误
言归正传:
除了调度和运行任务外,Tokio还提供异步执行输入和输出所需的一切。
tokio::io模块提供tokio的异步核心I/O原语,即AsyncRead、AsyncWrite和AsyncBufRead特性。此外,当启用“io-util”特性标志时,它还提供用于处理这些特性的组合子和函数,形成std::io的异步对应物。
Tokio还包括用于执行各种I/O以及与操作系统异步交互的API。其中包括:
tokio::net,它包含TCP、UDP和Unix域套接字的非阻塞版本(由“net”功能标志启用),
tokio::fs,类似于std::fs但用于异步执行文件系统I/O(由“fs”特性标志启用),
tokio::signal,用于异步处理Unix和Windows操作系统信号(由“signal”功能标志启用),
tokio::process,用于生成和管理子进程(由“process”特性标志启用)。
说实话这段话我没咋看懂什么东西
现在看一个简单的echo服务器
use tokio::net::TcpListener;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let listener = TcpListener::bind("127.0.0.1:8080").await?;
loop {
let (mut socket, _) = listener.accept().await?;
tokio::spawn(async move {
let mut buf = [0; 1024];
// In a loop, read data from the socket and write the data back.
loop {
let n = match socket.read(&mut buf).await {
// socket closed
Ok(n) if n == 0 => return,
Ok(n) => n,
Err(e) => {
eprintln!("failed to read from socket; err = {:?}", e);
return;
}
};
// Write the data back
if let Err(e) = socket.write_all(&buf[0..n]).await {
eprintln!("failed to write to socket; err = {:?}", e);
return;
}
}
});
}
}
妈的能不能看懂啊我日
我认为我没看明白是因为我语法学的不够透彻,我需要回去再看看基本的语法课,尤其是最后的服务器实现2024.4.7打个补丁
看懂了2024.4.9,loop里面match做模式匹配的时候会先等待await返回,然后进行模式匹配之后再去赋值
feature flags功能标志
Tokio使用一组功能标志来减少编译代码的数量。只启用某些功能而不启用其他功能是可能的。默认情况下,Tokio不启用任何功能,但允许为其用例启用子集。以下是可用功能标志的列表。您还可能注意到,在每个函数、结构和特征之上,都列出了要使用的项所需的一个或多个功能标志。如果您是Tokio的新手,建议您使用将启用所有公共API的完整功能标志。不过要注意,这会带来许多你可能不需要的额外依赖关系。
full:启用下面列出的除测试util和跟踪之外的所有功能。
rt:启用tokio::spawn、当前线程调度程序和非调度程序实用程序。
rt-multi-thread:启用更重的、多线程的、窃取工作的调度程序。
io-util:启用基于io的Ext特性。
io-std:启用Stdout、Stdin和Stderr类型。
net:启用tokio::net类型,如TcpStream、UnixStream和UdpSocket,以及(在类Unix系统上)AsyncFd和(在FreeBSD上)PollAio。
time:启用tokio::时间类型,并允许调度器启用内置计时器。
process:启用tokio::进程类型。
macros:启用#[tokio::main]和#[tokio::test]宏。
sync:启用所有tokio::sync类型。
signal:启用所有tokio::信号类型。
fs:启用tokio::fs类型。
test-util:为Tokio运行时启用基于测试的基础设施。
parking_lot:作为一种潜在的优化,在内部使用_parking_lot_crace的同步原语。此外,这种依赖关系对于在const上下文中构建我们的一些基元是必要的。MSRV可能会根据所使用的_parking_lot_版本而增加。
注意:AsyncRead和AsyncWrite特性不需要任何功能,并且始终可用。
unstable feature不稳定特征
某些功能标志仅在指定tokio_unstable标志时可用:
tracing:启用跟踪事件
同样,API的某些部分只能使用相同的标志:
task::Builder
Some methods on task::JoinSet
runtime::RuntimeMetrics
runtime::Builder::unhandled_panic
task::Id
此标志启用不稳定的功能。这些功能的公共API可能会在1.x版本中中断。要启用这些功能,编译时必须将–cfg tokio_unstable参数传递给rustc。这有助于明确选择可能违反semver惯例的功能,因为Cargo尚未直接支持此类选择加入。
You can specify it in your project’s .cargo/config.toml file:
你可以显示的在你的项目里cargo下的config.toml文件中添加
[build]
rustflags = ["--cfg", "tokio_unstable"]
或者,可以使用环境变量指定它:
## Many *nix shells:
export RUSTFLAGS="--cfg tokio_unstable"
cargo build
## Windows PowerShell:
$Env:RUSTFLAGS="--cfg tokio_unstable"
cargo build
支持的平台
Tokio当下保证支持如下平台:
Linux
Windows
Android (API level 21)
macOS
iOS
FreeBSD
Tokio将在未来继续支持这些平台。然而,未来的版本可能会更改要求,如Linux上最低要求的libc版本、Android上的API级别或支持的FreeBSD版本。
除了上述平台之外,Tokio还打算在mio机箱支持的所有平台上工作。你可以在mio的文档中找到一个更长的列表。但是,这些额外的平台将来可能会变得不受支持。
请注意,Wine被认为是与Windows不同的平台。有关Wine支持的更多信息,请参阅mio的文档。
WASM support
在开始这个了解之前我们首先了解一下什么是WASM
WebAssembly(简称WASM)是一种相对较新的技术,它允许开发者将用C、C++、Rust等语言编写的程序转换成一种特殊的代码,这种代码可以在网页浏览器中以非常高的效率运行。简单来说,WebAssembly让原本不是为Web设计的程序也能在网页上运行,而且运行得很快,几乎和本地应用程序一样快。
这种技术特别适合处理那些需要大量计算或者图形处理的任务,比如游戏、视频编辑软件、3D模型渲染等。因为WebAssembly的代码很小,所以它加载起来也很快,这对于提升用户体验很有帮助。
另外,WebAssembly和JavaScript可以一起工作,这意味着开发者可以结合使用这两种技术,利用JavaScript的灵活性和WebAssembly的高性能,共同构建更加强大的网页应用程序。而且,WebAssembly是开放标准的一部分,得到了各大浏览器厂商的支持,所以它的未来发展潜力非常大。
Tokio对WASM平台的支持有限。如果没有tokio_unstable标志,则支持以下功能:
sync
macros
io-util
rt
time
Re-exports转出口
pub use task::spawn; rt
什么鸟语
Modules模块
你还记得mod吗
doc:在Tokio crate中本地记录的类型,但实际上并不存在于此。
fs :异步文件和标准流自适应。
io:异步I/O功能的特征、辅助对象和类型定义。
net:用于tokio的TCP/UDP/Unix绑定。
process:Tokio异步进程管理的实现。
tokio:tokio运行时
signal:Tokio的异步信号处理。
steam:由于Stream特性包含在比Tokio的1.0更晚的std着陆版本中,大多数Tokio流实用程序都已移动到Tokio流crate中。
syn:用于异步上下文的同步原语
task:异步绿色线程
time:用于跟踪时间的程序
Macros
join:在多个并发分支上等待,当所有分支完成时返回。
pin:在堆栈上固定一个值。
select:在多个并发分支上等待,当第一个分支完成时返回,取消其余分支。
task_local:声明一个tokio::task::LocalKey类型的task-local key
try_join:在多个并发分支上等待,当所有分支都完成Ok()或第一个Err()时返回。
Attribute Macros属性宏
main:将异步函数标记为由所选运行时执行。此宏有助于设置运行时,而无需用户直接使用运行时或生成器。
test:标记异步函数由运行时执行,适合测试环境。此宏有助于设置运行时,而无需用户直接使用运行时或生成器。