4个版本
0.1.3 | 2021年10月4日 |
---|---|
0.1.2 | 2021年9月29日 |
0.1.1 | 2021年9月28日 |
0.1.0 | 2021年9月28日 |
在异步类别中排名915
每月下载2,306次
在9个crate(3个直接)中使用
39KB
254 行
tokio-context
提供两种取消future的方法,并提供一个用于取消所有相关future的处理句柄,同时具有回退超时机制。这可以通过Context
API或TaskController
API来实现,具体取决于用户的需求。
上下文
提供类似于Golang的上下文功能。在这个意义上,上下文是一个传递的对象,主要用于异步函数,用于确定长运行的异步任务是否应该继续运行或终止。
您可以通过调用其new
构造函数来构建一个新的上下文,它返回一个新的Context
和一个Handle
。可以调用Handle
的cancel
方法,或者简单地丢弃它来取消上下文。
请注意,丢弃Handle
将取消上下文。
如果您想创建一个在给定持续时间后自动取消的上下文,请使用with_timeout
构造函数。使用此构造函数将仍然为您提供可以用来立即取消上下文的句柄。
示例
use tokio::time;
use tokio_context::context::Context;
use std::time::Duration;
async fn task_that_takes_too_long() {
time::sleep(time::Duration::from_secs(60)).await;
println!("done");
}
#[tokio::main]
async fn main() {
// We've decided that we want a long running asynchronous task to last for a maximum of 1
// second.
let (mut ctx, _handle) = Context::with_timeout(Duration::from_secs(1));
tokio::select! {
_ = ctx.done() => return,
_ = task_that_takes_too_long() => panic!("should never have gotten here"),
}
}
虽然这可能看起来与简单地使用tokio::time::timeout
没有区别,但我们保留了可以用来显式取消上下文以及任何附加生成的上下文的句柄。
use std::time::Duration;
use tokio::time;
use tokio::task;
use tokio_context::context::Context;
async fn task_that_takes_too_long(mut ctx: Context) {
tokio::select! {
_ = ctx.done() => println!("cancelled early due to context"),
_ = time::sleep(time::Duration::from_secs(60)) => println!("done"),
}
}
#[tokio::main]
async fn main() {
let (_, mut handle) = Context::new();
let mut join_handles = vec![];
for i in 0..10 {
let mut ctx = handle.spawn_ctx();
let handle = task::spawn(async { task_that_takes_too_long(ctx).await });
join_handles.push(handle);
}
// Will cancel all spawned contexts.
handle.cancel();
// Now all join handles should gracefully close.
for join in join_handles {
join.await.unwrap();
}
}
通过结合使用with_parent
构造函数和RefContexts,也可以将上下文链接起来。将上下文链接起来意味着如果父上下文被取消,则上下文也将被取消。一个RefContext
只是对Arc<Mutex<Context>>
的简单包装,它具有与Context
相同的API。以下是一些示例,演示可链接上下文的工作方式
use std::time::Duration;
use tokio::time;
use tokio::task;
use tokio_context::context::RefContext;
#[tokio::test]
async fn cancelling_parent_ctx_cancels_child() {
// Note that we can't simply drop the handle here or the context will be cancelled.
let (parent_ctx, parent_handle) = RefContext::new();
let (mut ctx, _handle) = Context::with_parent(&parent_ctx, None);
parent_handle.cancel();
// Cancelling a parent will cancel the child context.
tokio::select! {
_ = ctx.done() => assert!(true),
_ = tokio::time::sleep(Duration::from_millis(15)) => assert!(false),
}
}
#[tokio::test]
async fn cancelling_child_ctx_doesnt_cancel_parent() {
// Note that we can't simply drop the handle here or the context will be cancelled.
let (mut parent_ctx, _parent_handle) = RefContext::new();
let (_ctx, handle) = Context::with_parent(&parent_ctx, None);
handle.cancel();
// Cancelling a child will not cancel the parent context.
tokio::select! {
_ = parent_ctx.done() => assert!(false),
_ = async {} => assert!(true),
}
}
#[tokio::test]
async fn parent_timeout_cancels_child() {
// Note that we can't simply drop the handle here or the context will be cancelled.
let (parent_ctx, _parent_handle) = RefContext::with_timeout(Duration::from_millis(5));
let (mut ctx, _handle) =
Context::with_parent(&parent_ctx, Some(Duration::from_millis(10)));
tokio::select! {
_ = ctx.done() => assert!(true),
_ = tokio::time::sleep(Duration::from_millis(7)) => assert!(false),
}
}
如果您的子future需要了解取消信号,则上下文模式非常有用。这在许多子future需要执行优雅终止的情况中非常有用。
在不需要子future优雅终止的情况下,使用TaskController
提供的API会更方便。它不会使子future受到额外的上下文函数参数的干扰。但是,它将执行突然的未来终止,这可能不是始终希望的结果。
TaskController
负责启动任务,可以通过在任务控制器上调用cancel
来取消这些任务。如果使用with_timeout
构造函数提供了std::time::Duration
,则TaskController启动的任何任务将在指定的时间过后自动取消。
这为相同的最终结果提供了与Context不同的API。当不需要子future优雅关闭时,它更易于使用。如果您确实需要子future优雅关闭,则需要向下传递上下文,并将上下文纳入子函数的正常程序流程,以便它们可以根据需要进行响应,并执行自定义异步清理逻辑。
示例
use std::time::Duration;
use tokio::time;
use tokio_context::task::TaskController;
async fn task_that_takes_too_long() {
time::sleep(time::Duration::from_secs(60)).await;
println!("done");
}
#[tokio::main]
async fn main() {
let mut controller = TaskController::new();
let mut join_handles = vec![];
for i in 0..10 {
let handle = controller.spawn(async { task_that_takes_too_long().await });
join_handles.push(handle);
}
// Will cancel all spawned contexts.
controller.cancel();
// Now all join handles should gracefully close.
for join in join_handles {
join.await.unwrap();
}
}
许可:MIT
依赖项
~2.3–4MB
~64K SLoC