5 个稳定版本
1.1.1 | 2024 年 5 月 21 日 |
---|---|
1.1.0 | 2024 年 5 月 6 日 |
1.0.2 | 2024 年 5 月 5 日 |
1.0.1 | 2024 年 5 月 4 日 |
#2131 在 异步
每月 35 次下载
用于 4 crates
50KB
1K SLoC
Spawns
Rust 的线程上下文任务创建器,简化异步运行时不依赖的编码。
动机
目前,Rust 没有标准的异步运行时。这使我们面临一个困境,需要选择一个,并使得创建不依赖于运行时的库变得相当困难。我们面临的最大挑战是如何创建任务?
spawns
为 Rust std
和异步运行时提供了一个线程上下文任务创建器。一旦部署,我们就可以以不依赖于运行时的方式创建任务。与其他不依赖于运行时的 io、定时器、通道等库一起,我们可以轻松地编写不依赖于运行时的代码。
异步运行时的 API
/// Thin wrapper around task to accommodate possible new members.
#[non_exhaustive]
pub struct Task {
pub id: Id,
pub name: Name,
pub future: Box<dyn Future<Output = ()> + Send + 'static>,
}
/// Trait to spawn task.
pub trait Spawn {
fn spawn(&self, task: Task);
}
/// Scope where tasks are [spawn]ed through given [Spawn].
pub struct SpawnScope<'a> {}
/// Enters a scope where new tasks will be [spawn]ed through given [Spawn].
pub fn enter(spawner: &dyn Spawn) -> SpawnScope<'_>;
异步运行时必须执行两项操作,以适应其他不依赖于运行时的 API。
- 实现
Spawn
以创建异步任务。 - 在所有执行线程中调用
enter
。
客户端的 API
impl<T> JoinHandle<T> {
/// Gets id of the associated task.
pub fn id(&self) -> Id {}
/// Cancels associated task with this handle.
///
/// Cancellation is inherently concurrent with task execution. Currently, there is no guarantee
/// about promptness, the task could even run to complete normally after cancellation.
pub fn cancel(&self) { }
/// Attaches to associated task to gain cancel on [Drop] permission.
pub fn attach(self) -> TaskHandle<T> { }
}
impl<T> Future for JoinHandle<T> {
type Output = Result<T, JoinError>;
}
/// Spawns a new task.
///
/// # Panics
/// 1. Panic if no spawner.
/// 2. Panic if [Spawn::spawn] panic.
pub fn spawn<T, F>(f: F) -> JoinHandle<T>
where
F: Future<Output = T> + Send + 'static,
T: Send + 'static;
该 API 能够像 tokio
、smol
和 async-std
一样创建、连接和取消任务。
关注点
- 装箱?是的,它需要
GlobalAlloc
。 - 即使是入口 future 也装箱?不,但
try_id()
将返回None
。我想我们可以提供一些包装函数。 no_std
?不,它目前需要thread_local!
。一旦稳定,我们可以将其移动到#[thread_local]
。- 对于
spawn_local
和!Send
future,目前至少不是这样。我只看到async-global-executor
能够自由地使用spawn_local
。我认为这是Rust的责任,不应该将拥有!Send
的futures视为!Send
。这样我们就不太可能创建出!Send
futures。参见Async Rust needs Await and 'thread forSend
Future
,了解我对这一问题的看法。对于首先捕获!Send
并在线程局部存储!Send
的futures,它们需要当前线程的executor。
软件包
- spawns-core为异步运行时提供
Spawn
和enter()
来设置线程上下文任务spawner。 - spawns-compat通过功能门提供对
tokio
、smol
和async-global-executor
(由async-std
使用)的兼容性。 - spawns-executor提供带有当前线程executor和多线程executor的完整功能
block_on
。 - spawns导出上述所有软件包,包括功能门
tokio
、smol
和async-global-executor
。此外,它还提供功能门executor
以包含spawns-executor
。
示例
请参阅示例。这里列出一个最小的、与运行时无关的echo服务器以供演示。
use async_net::*;
use futures_lite::io;
pub async fn echo_server(port: u16) {
let listener = TcpListener::bind(("127.0.0.1", port)).await.unwrap();
println!("Listen on port: {}", listener.local_addr().unwrap().port());
let mut echos = vec![];
let mut id_counter = 0;
loop {
let (stream, remote_addr) = listener.accept().await.unwrap();
id_counter += 1;
let id = id_counter;
let handle = spawns::spawn(async move {
eprintln!("{:010}[{}]: serving", id, remote_addr);
let (reader, writer) = io::split(stream);
match io::copy(reader, writer).await {
Ok(_) => eprintln!("{:010}[{}]: closed", id, remote_addr),
Err(err) => eprintln!("{:010}[{}]: {:?}", id, remote_addr, err),
}
})
.attach();
echos.push(handle);
}
}
要使它功能正常,您只需设置线程上下文任务spawner。
许可证
依赖关系
~110KB