当前位置:首页 > 科技  > 软件

我们一起聊聊如何编写异步运行时通用库?

来源: 责编: 时间:2024-05-07 09:15:18 220观看
导读如果你正在用Rust编写异步应用程序,在某些情况下,你可能希望将代码分成几个子crate。这样做的好处是:更好的封装,在子系统之间有一个crate边界,可以产生更清晰的代码和定义更良好的API。不再需要这样写:pub(crate)。更快的

如果你正在用Rust编写异步应用程序,在某些情况下,你可能希望将代码分成几个子crate。这样做的好处是:Qup28资讯网——每日最新资讯28at.com

  • 更好的封装,在子系统之间有一个crate边界,可以产生更清晰的代码和定义更良好的API。不再需要这样写:pub(crate)。
  • 更快的编译,通过将一个大crate分解成几个独立的小crate,它们可以并发地编译。

使用一个异步运行时,编写异步运行时通用库的好处是什么?Qup28资讯网——每日最新资讯28at.com

  • 可移植性,你可以很容易地切换到不同的异步运行时或wasm。
  • 保证正确性,针对tokio和async-std,测试一个库就可以发现更多的bug,包括并发bug(由于任务执行顺序模糊)和“未定义行为”(由于误解异步运行时实现细节)

下面使用三种方法来实现异步运行时通用库。Qup28资讯网——每日最新资讯28at.com

方法1,定义自己的异步运行时TraitQup28资讯网——每日最新资讯28at.com

使用futures crate,可以编写非常通用的库代码,但是time,sleep或timeout等操作必须依赖于异步运行时。这时,你可以定义自己的AsyncRuntime trait,并要求下游实现它。Qup28资讯网——每日最新资讯28at.com

use std::{future::Future, time::Duration};pub trait AsyncRuntime: Send + Sync + 'static {    type Delay: Future<Output = ()> + Send;    // 返回值必须是一个Future    fn sleep(duration: Duration) -> Self::Delay;}

可以像这样使用上面的库代码:Qup28资讯网——每日最新资讯28at.com

async fn operation<R: AsyncRuntime>() {    R::sleep(Duration::from_millis(1)).await;}

下面是它如何实现的:Qup28资讯网——每日最新资讯28at.com

pub struct TokioRuntime;impl AsyncRuntime for TokioRuntime {    type Delay = tokio::time::Sleep;    fn sleep(duration: Duration) -> Self::Delay {        tokio::time::sleep(duration)    }}#[tokio::main]async fn main() {    operation::<TokioRuntime>().await;    println!("Hello, world!");}

方法2,在内部抽象异步运行时并公开特性标志Qup28资讯网——每日最新资讯28at.com

为了处理网络连接或文件句柄,我们可以使用AsyncRead / AsyncWrite trait:Qup28资讯网——每日最新资讯28at.com

#[async_trait]pub(crate) trait AsyncRuntime: Send + Sync + 'static {    type Connection: AsyncRead + AsyncWrite + Send + Sync + 'static;    async fn connect(addr: SocketAddr) -> std::io::Result<Self::Connection>;}

可以像这样使用上面的库代码:Qup28资讯网——每日最新资讯28at.com

async fn operation<R: AsyncRuntime>(conn: &mut R::Connection) where    R::Connection: Unpin,{    conn.write(b"some bytes").await;}

然后为每个异步运行时定义一个模块:Qup28资讯网——每日最新资讯28at.com

#[cfg(feature = "runtime-async-std")]mod async_std_impl;#[cfg(feature = "runtime-async-std")]use async_std_impl::*;#[cfg(feature = "runtime-tokio")]mod tokio_impl;#[cfg(feature = "runtime-tokio")]use tokio_impl::*;

tokio_impl模块:Qup28资讯网——每日最新资讯28at.com

mod tokio_impl {    use std::net::SocketAddr;    use async_trait::async_trait;    use crate::AsyncRuntime;    pub struct TokioRuntime;    #[async_trait]    impl AsyncRuntime for TokioRuntime {        type Connection = tokio::net::TcpStream;        async fn connect(addr: SocketAddr) -> std::io::Result<Self::Connection> {            tokio::net::TcpStream::connect(addr).await        }    }}

main函数代码:Qup28资讯网——每日最新资讯28at.com

#[tokio::main]async fn main() {    let mut conn =        TokioRuntime::connect(SocketAddr::new(IpAddr::from_str("0.0.0.0").unwrap(), 8080))            .await            .unwrap();    operation::<TokioRuntime>(&mut conn).await;    println!("Hello, world!");}

方法3,维护一个异步运行时抽象库Qup28资讯网——每日最新资讯28at.com

基本上,将使用的所有异步运行时api写成一个包装器库。这样做可能很繁琐,但也有一个好处,即可以在一个地方为项目指定与异步运行时的所有交互,这对于调试或跟踪非常方便。Qup28资讯网——每日最新资讯28at.com

例如,我们定义异步运行时抽象库的名字为:common-async-runtime,它的异步任务处理代码如下:Qup28资讯网——每日最新资讯28at.com

// common-async-runtime/tokio_task.rspub use tokio::task::{JoinHandle as TaskHandle};pub fn spawn_task<F, T>(future: F) -> TaskHandle<T>where    F: Future<Output = T> + Send + 'static,    T: Send + 'static,{    tokio::task::spawn(future)}

async-std的任务API与Tokio略有不同,这需要一些样板文件:Qup28资讯网——每日最新资讯28at.com

// common-async-runtime/async_std_task.rspub struct TaskHandle<T>(async_std::task::JoinHandle<T>);pub fn spawn_task<F, T>(future: F) -> TaskHandle<T>where    F: Future<Output = T> + Send + 'static,    T: Send + 'static,{    TaskHandle(async_std::task::spawn(future))}#[derive(Debug)]pub struct JoinError;impl std::error::Error for JoinError {}impl<T> Future for TaskHandle<T> {    type Output = Result<T, JoinError>;    fn poll(        mut self: std::pin::Pin<&mut Self>,        cx: &mut std::task::Context<'_>,    ) -> std::task::Poll<Self::Output> {        match self.0.poll_unpin(cx) {            std::task::Poll::Ready(res) => std::task::Poll::Ready(Ok(res)),            std::task::Poll::Pending => std::task::Poll::Pending,        }    }}

在Cargo.toml中,你可以简单地将common-async-runtime作为依赖项包含进来。这使得你的库代码很“纯粹”,因为现在选择异步运行时是由下游控制的。与方法1类似,这个crate可以在没有任何异步运行时的情况下编译,这很简洁!Qup28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-87039-0.html我们一起聊聊如何编写异步运行时通用库?

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: Python 推导式在接口自动化里的运用

下一篇: .NET中的多线程超时处理实践

标签:
  • 热门焦点
  • 一加Ace2 Pro官宣:普及16G内存 引领24G

    一加官方今天继续为本月发布的新机一加Ace2 Pro带来预热,公布了内存方面的信息。“淘汰 8GB ,12GB 起步,16GB 普及,24GB 引领,还有呢?#一加Ace2Pro#,2023 年 8 月,敬请期待。”同时
  • K60至尊版狂暴引擎2.0加持:超177万跑分斩获性能第一

    Redmi的后性能时代战略发布会今天下午如期举办,在本次发布会上,Redmi公布了多项关于和联发科的深度合作,以及新机K60 Ultra在软件和硬件方面的特性,例如:“K60 至尊版,双芯旗舰
  • 7月安卓手机好评榜:三星S23Ultra好评率第一

    性能榜和性价比榜之后,我们来看最后的安卓手机好评榜,数据来源安兔兔评测,收集时间2023年7月1日至7月31日,仅限国内市场。第一名:三星Galaxy S23 Ultra好评率:95.71%在即将迎来新
  • K8S | Service服务发现

    一、背景在微服务架构中,这里以开发环境「Dev」为基础来描述,在K8S集群中通常会开放:路由网关、注册中心、配置中心等相关服务,可以被集群外部访问;图片对于测试「Tes」环境或者
  • 在线图片编辑器,支持PSD解析、AI抠图等

    自从我上次分享一个人开发仿造稿定设计的图片编辑器到现在,不知不觉已过去一年时间了,期间我经历了裁员失业、面试找工作碰壁,寒冬下一直没有很好地履行计划.....这些就放在日
  • JVM优化:实战OutOfMemoryError异常

    一、Java堆溢出堆内存中主要存放对象、数组等,只要不断地创建这些对象,并且保证 GC Roots 到对象之间有可达路径来避免垃 圾收集回收机制清除这些对象,当这些对象所占空间超过
  • 冯提莫签约抖音公会 前“斗鱼一姐”消失在直播间

    来源:直播观察提起&ldquo;冯提莫&rdquo;这个名字,很多网友或许听过,但应该不记得她是哪位主播了。其实,作为曾经的&ldquo;斗鱼一姐&rdquo;,冯提莫在游戏直播的年代影响力不输于现
  • 支持aptX Lossless无损传输 iQOO TWS 1赛道版发布限时优惠价369元

    2023年7月4日,“无损音质,声动人心”iQOO TWS 1正式发布,支持aptX Lossless无损传输,限时优惠价369元。iQOO TWS 1耳机率先支持端到端aptX Lossless无
  • 微软发布Windows 11新版 引入全新任务栏状态

    近日,微软发布了Windows 11新版,而Build 22563更新主要引入了几周前曝光的平板模式任务栏等,系统更流畅了。更新中,Windows 11加入了专门针对平板优化的任务栏
Top