从浅到深-Tokio强大的Rust异步框架,强大的异步IO
从浅到深-Tokio强大的Rust异步框架,强大的异步IO
大家好,我是梦兽编程。欢迎回来与梦兽编程一起刷Rust的系列。微信公众号【梦兽编程】即可加入梦兽编程微信交流群与我交流。
语言只是我们程序员实现途径中的工具,语言的生态丰富会让我们做起事来事半功倍。从今天开始我们开始进入Rust的生态学习。
在Rust中同样支持异步编程,只不过一般我们不会使用Rust官方,而是使用第三方依赖Tokio。今天就我们进入新的篇章《Tokio从浅到深之旅》。
IO
在计算机中IO是以个非常重要的指标,它决定你的处理速度。计算机无法就是数据的输入/输出。所谓的IO就是input/output的缩写。
同样Rust语言的标准库中也是提供了非常多的IO操作,例如TCP连接,UDP套接字,读取和写入文件等。这些操作都是同步或阻塞的,这意味着当它们被调用时,当前线程可能会停止执行并进入睡眠状态,直到它被解除阻塞。
如果有非常庞大的输入数据需要处理时,等待处理的时间将非常,上一次的分享中。使用Future的时候,这种行为会有问题,因为我们希望在等待I/O完成时继续执行我们可能拥有的其他Future。但是这些工作Tokio已经为我们实现了。今天就让我们一起学习Tokio中常用的异步IO操作。
AsyncRead和AsyncWrite
在Tokio中提供了两个traits分别AsyncRead和AsyncWrite。这两个 traits 分别继承自 std::io
中的 Read
和 Write
,并添加了非阻塞的特性。在无可用数据时,所有非阻塞 I/O 对象都必须返回一个错误而不是阻塞当前线程。
use std::io::Read;
pub trait AsyncRead: Read {
// ...
// various provided methods
// ...
}
// 核心的实现Future
fn poll_read(&mut self, buf: &mut [u8]) -> Poll<usize, std::io::Error> {
match self.read(buf) {
Ok(t) => Ok(Async::Ready(t)),
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
Ok(Async::NotReady)
}
Err(e) => Err(e),
}
}
异步打开文件
如果你想使用异步读写文件,只需要把std::io
都替换成tokio::io
即可,比如我们需要打开一个文件。
use tokio::fs::File;
use tokio::io::{self, AsyncReadExt};
use tokio::runtime::Runtime;
#[tokio::main]
async fn main() -> io::Result<()> {
let file_path = "path/to/your/file.txt";
let mut file = File::open(file_path).await?;
let mut contents = String::new();
file.read_to_string(&mut contents).await?;
println!("File contents:\n{}", contents);
Ok(())
}
需要注意的是你未来的所有操作都需要添加
await
拆分 I/O
通常指的是将一个单一的 I/O 资源(如一个 TCP 流)分成两个独立的半部分:一个用于读取(ReadHalf
)和一个用于写入(WriteHalf
)。这样做的目的是为了能够在不同的任务中独立地处理读和写操作,从而提高并发处理的效率。
例如,对于一个 TcpStream
,你可以使用 split
方法将其拆分为两个部分:
use tokio::net::TcpStream;
use tokio::io::{self, AsyncReadExt, AsyncWriteExt};
#[tokio::main]
async fn main() -> io::Result<()> {
let stream = TcpStream::connect("127.0.0.1:8080").await?;
let (read_half, write_half) = stream.split();
// 现在可以在不同的任务中独立地使用 read_half 和 write_half
// 例如,一个任务可以读取数据,而另一个任务可以写入数据
Ok(())
}
拆分后我们得到两个心跳,你可以分别把读和写分别放到两个异步管理器中单独运行。
// 在一个任务中处理读取操作
tokio::spawn(async move {
let mut reader = read_half;
let mut buffer = [0; 1024];
loop {
let n = reader.read(&mut buffer).await.unwrap();
if n == 0 {
break; // 连接已关闭
}
println!("Received: {}", String::from_utf8_lossy(&buffer[..n]));
}
});
// 在另一个任务中处理写入操作
tokio::spawn(async move {
let mut writer = write_half;
let message = "Hello, world!".as_bytes();
writer.write_all(message).await.unwrap();
});
转载自:https://juejin.cn/post/7353160406652862527