#async-executor #executor #future #async #async-await

async_executors

实现了常见执行器的 Spawn、SpawnLocal 和 SpawnHandle

18 个版本

0.7.0 2023 年 7 月 22 日
0.6.0 2022 年 4 月 24 日
0.5.1 2022 年 1 月 6 日
0.4.2 2021 年 6 月 10 日
0.0.0 2019 年 10 月 28 日

#432异步

Download history 1838/week @ 2024-03-13 2708/week @ 2024-03-20 1916/week @ 2024-03-27 2077/week @ 2024-04-03 1961/week @ 2024-04-10 1488/week @ 2024-04-17 1859/week @ 2024-04-24 1771/week @ 2024-05-01 1425/week @ 2024-05-08 1787/week @ 2024-05-15 1591/week @ 2024-05-22 1884/week @ 2024-05-29 1494/week @ 2024-06-05 2115/week @ 2024-06-12 2856/week @ 2024-06-19 2421/week @ 2024-06-26

9,272 每月下载量
用于 51 个 crate (12 个直接)

Unlicense

95KB
1.5K SLoC

async_executors

standard-readme compliant Build Status Docs crates.io

抽象不同的执行器。

async_executors 的目标是帮助您编写与执行器无关的库。我们通过 traits 表达常见的执行器功能,并为最常用的执行器实现它。这样,库可以根据需要要求确切的功能,而客户端代码可以使用它们选择的任何执行器,只要它可以提供所需的功能。

可用的 traits 被分组在 iface 模块中。我们还实现了来自 futuresSpawn 和/或 LocalSpawn traits。

所有支持的执行器都通过 features 开启,见下文。

目录

安装

使用 cargo add: cargo add async_executors

使用 cargo yaml

dependencies:

   async_executors: ^0.7

使用 Cargo.toml

[dependencies]

    async_executors = "0.7"

升级

升级时请查看 变更日志

依赖项

此 crate 有少量依赖项。Cargo 将自动为您处理其依赖项。

唯一的硬依赖项是 futures-taskfutures-util。其余的都是为每个执行器开启支持的可选依赖项。

功能

此 crate 有很多功能。让我们来看看它们。

通用功能

  • tracing:当启用时,所有特性都会为tracing-futures::Instrumentedtracing-futures::WithDispatch重新实现。
  • timer:开启futures-timer包。这允许执行器进行异步休眠。在tokio上,您可以选择启用tokio_timer以启用tokio原生定时器。async_std,当不在wasm上时,提供定时器而不需要此功能。

执行器特定

  • async_global:开启来自async-global-executor的执行器。支持Wasm和!Send任务。
  • async_global_tokio:确保在AsyncGlobal上派生的任务有一个tokio反应器运行。AsyncGlobal将实现TokioIo特质。
  • async_std:开启来自async-std包的执行器。支持Wasm和!Send任务。
  • async_std_tokio:确保在AsyncStd上派生的任务有一个tokio反应器运行。AsyncStd将实现TokioIo特质。
  • glommio:开启来自glommio包的执行器。单线程,仅支持Linux 5.8+。支持!Send任务。
  • tokio_ct:Tokio 当前线程,启用tokio包的单线程运行时。支持!Send任务。
  • tokio_tp:Tokio 线程池,启用tokio包的线程池运行时。
  • tokio_timer:将在tokio上启用time特性,并在创建的任何tokio运行时上调用enable_time()。对于tokio运行时,此特性优先于timer特性。
  • tokio_io:将在tokio上启用netprocess特性,并在创建的任何tokio运行时上调用enable_reactor()TokioCtTokioTp将实现TokioIo特质。
  • localpool : 启用来自 futures-executor 的单线程执行器。支持 !Send 任务。将重新导出 LocalPoolLocalSpawner,并实现我们的特质。
  • threadpool : 启用来自 futures-executor 的线程池执行器。将重新导出 ThreadPool,并实现我们的特质。
  • bindgen : 启用来自 wasm-bindgen-futures 的单线程执行器。仅适用于 Wasm。支持 !Send 任务。

安全性

该包本身使用 #[ forbid(unsafe_code) ]

我们的依赖项使用不安全代码。

性能

大多数包装器非常简洁,但 SpawnLocalSpawn 特质确实意味着对未来的装箱。由于执行器将未来装箱以将其放入队列,因此每个 spawn 可能需要 2 个堆分配。

JoinHandle 使用来自 tokioasync-std 的本地 JoinHandle 类型来避免 RemoteHandle 的开销,但对于 async-std,将未来包装在 Abortable 中以在所有执行器之间创建一致的行为。提供的 JoinHandle 在丢弃时会取消其未来,除非你调用其 detach

SpawnHandleLocalSpawnHandle 需要对未来进行两次装箱,就像 SpawnLocalSpawn 一样。

所有执行器的现有基准测试可以在 executor_benchmarks 中找到。

使用

对于 API 提供者

当编写需要 spawn 的库时,你很可能不希望将客户端锁定在某个框架上。通常,设置自己的线程池来 spawn 未来是不恰当的。决定在哪里 spawn 未来是应用程序开发者的责任,如果库引入了对框架的额外依赖,可能会不受欢迎。

为了解决这个问题,你可以从客户端代码中接受一个执行器作为参数,并在提供的执行器上 spawn 你的未来。目前,只有两个广泛可用的特质是 futures 库中的 SpawnLocalSpawn。不幸的是,其他执行器提供者没有实现这些特质。因此,通过发布依赖于这些特质的 API,你会限制客户端只能使用 futures 的执行器,或者开始实现自己的包装器,该包装器实现这些特质。

Async_executors 提供了在各个执行器上实现包装器的包装器,例如 tokioasync_stdwasm_bindgen 等。因此,你可以只使用特质界限,并在用户想要使用支持的任何执行器时将其指向此包。

所有包装器也实现了 CloneDebug,以及无尺寸的包装器也实现了 Copy。你可以在你的 API 中表达你需要克隆:impl Spawn + Clone

请注意,你绝对不应该在异步上下文中使用 block_on。根据执行器,这可能会挂起或崩溃。我们使用的某些后端,如 tokiofutures 中的 RemoteHandle,使用 catch_unwind,因此请尽量保持未来的 unwind 安全。

使用句柄进行 spawn

您可以使用 SpawnHandleLocalSpawnHandle 特性作为获取连接句柄的边界。

示例
use
{
  async_executors :: { JoinHandle, SpawnHandle, SpawnHandleExt       } ,
  std             :: { sync::Arc                                     } ,
  futures         :: { FutureExt, executor::{ ThreadPool, block_on } } ,
};


// Example of a library function that needs an executor. Just use impl Trait.
//
fn needs_exec( exec: impl SpawnHandle<()> )
{
   let handle = exec.spawn_handle( async {} );
}


// A type that needs to hold on to an executor during it's lifetime. Here it
// must be heap allocated.
//
struct SomeObj{ exec: Arc< dyn SpawnHandle<u8> > }


impl SomeObj
{
   pub fn new( exec: Arc< dyn SpawnHandle<u8> > ) -> SomeObj
   {
      SomeObj{ exec }
   }

   fn run( &self ) -> JoinHandle<u8>
   {
      self.exec.spawn_handle( async{ 5 } ).expect( "spawn" )
   }
}


fn main()
{
  let exec = ThreadPool::new().expect( "build threadpool" );
  let obj  = SomeObj::new( Arc::new(exec) );

  let x = block_on( obj.run() );

  assert_eq!( x, 5 );
}

如上述示例所示,future 的输出是 SpawnHandle 的类型参数。这是必要的,因为如果将其放在方法上,则会使特性不再对象安全,这意味着除非作为类型参数,否则无法存储。

定义您需要的组合能力最好的方式是创建自己的特性别名(这里通过来自 trait_set 包的宏展示,但您也可以根据需要编写 blanket 实现)

use async_executors::*;

trait_set::trait_set!
{
   pub trait LibExec = SpawnHandle<()> + SpawnHandle<u8> + Timer + YieldNow + Clone;
}

pub fn lib_function( _exec: impl LibExec ) {}

SpawnHandle 的所有实现者都必须支持任何输出类型。因此,向 LibExec 添加更多的 SpawnHandle 边界不应破坏兼容性。

对于 API 消费者

基本上,您可以将 async_executors 中提供的包装类型传递给接受以下任何类型的 API。还实现了 RcArc&&mutBox 和来自 tracing-futuresInstrumentedWithDispatch 的特性。

  • impl Spawn
  • impl LocalSpawn
  • impl SpawnHandle<T>
  • impl LocalSpawnHandle<T>
  • impl SpawnBlocking
  • impl YieldNow
  • impl Timer
  • impl TokioIo

所有包装器也实现了 CloneDebug,并且零大小类型也实现了 Copy

一些执行器有些特别,所以请确保查看您打算使用的 API 文档。一些还提供了额外的功能,如 block_on 方法,这将调用特定框架的 block_on 而不是来自 futuresblock_on

示例

use
{
  async_executors :: { AsyncStd, SpawnHandle, TokioTp } ,
  std             :: { convert::TryFrom               } ,
};

fn needs_exec( exec: impl SpawnHandle<()> + SpawnHandle<String> ){};

// AsyncStd is zero sized, so it's easy to instantiate.
//
needs_exec( AsyncStd );

// We need a builder type for tokio, as we guarantee by the type of TokioTp that it
// will be a threadpool.
//
let tp = TokioTp::new().expect( "build threadpool" );

needs_exec( tp );

有关更多示例,请查看 示例目录。如果您想获得一个更完善的 API 来遵守结构化并发,请查看 async_nursery

API

API 文档可在 docs.rs 上找到。

贡献

请查看 贡献指南

测试

运行 ci/test.bashci/wasm.bash 以运行所有测试。

行为准则

公民行为准则第 4 点“不可接受的行为”中描述的任何行为在这里都不受欢迎,并可能导致您被禁止。如果任何包括维护者和项目管理员在内的个人未能遵守这些/您的限制,您有权对他们进行指责。

许可

未授权许可

依赖项

~1–18MB
~204K SLoC