Rust异步信号处理库async-ctrlc的使用:优雅处理Ctrl-C中断的跨平台解决方案
Rust异步信号处理库async-ctrlc的使用:优雅处理Ctrl-C中断的跨平台解决方案
async-ctrlc
是ctrlc
crate的异步包装器,提供了优雅处理Ctrl+C中断信号的跨平台解决方案。
Future示例
use async_ctrlc::CtrlC;
use async_std::{prelude::FutureExt, task::sleep};
use std::time::Duration;
#[async_std::main]
async fn main() {
// 创建Ctrl+C处理器
let ctrlc = CtrlC::new().expect("cannot create Ctrl+C handler?");
println!("Try to press Ctrl+C");
// 使用race方法在Ctrl+C信号和异步任务之间竞争
ctrlc.race(async {
let mut i = 0;
loop {
println!("... {}", i);
sleep(Duration::from_secs(1)).await;
i += 1;
}
}).await;
println!("Quitting");
}
Stream示例
use async_ctrlc::CtrlC;
use async_std::prelude::StreamExt as _;
#[async_std::main]
async fn main() {
// 创建Ctrl+C处理器
let ctrlc = CtrlC::new().expect("cannot create Ctrl+C handler?");
println!("Try to press Ctrl+C 3 times");
// 将Ctrl+C信号转换为流,并只取前3次
let mut stream = ctrlc.enumerate().take(3);
// 处理每次Ctrl+C信号
while let Some((count, _)) = stream.next().await {
println!("{} x Ctrl+C!", count + 1);
}
println!("Quitting");
}
完整示例代码
下面是一个更完整的示例,展示了如何在实际应用中使用async-ctrlc
:
use async_ctrlc::CtrlC;
use async_std::{prelude::*, task};
use std::time::Duration;
#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 创建Ctrl+C处理器
let ctrlc = CtrlC::new()?;
// 模拟长时间运行的任务
let long_running_task = async {
let mut counter = 0;
loop {
println!("Working on task {}...", counter);
task::sleep(Duration::from_secs(1)).await;
counter += 1;
// 模拟任务完成条件
if counter == 10 {
println!("Task completed successfully!");
break;
}
}
};
println!("Application started. Press Ctrl+C to interrupt or wait for task to complete.");
// 使用race方法处理中断
ctrlc.race(long_running_task).await;
// 清理资源
println!("Performing cleanup...");
task::sleep(Duration::from_secs(1)).await;
println!("Application exited gracefully.");
Ok(())
}
特性
- 跨平台支持:在Windows、Linux和macOS上都能工作
- 异步集成:与async-std和tokio等异步运行时无缝集成
- 灵活的使用模式:
- 作为Future使用,与任务竞争
- 作为Stream使用,处理多次中断信号
- 轻量级:库大小仅约12.1 KiB
安装
在项目目录中运行以下Cargo命令:
cargo add async-ctrlc
或者在Cargo.toml中添加:
async-ctrlc = "1.2.0"
许可证
MIT或Apache-2.0。
1 回复
Rust异步信号处理库async-ctrlc的使用:优雅处理Ctrl-C中断的跨平台解决方案
async-ctrlc
是一个Rust库,提供了跨平台的异步Ctrl-C信号处理功能,特别适合需要优雅关闭的异步应用程序。
主要特性
- 跨平台支持(Windows/Linux/macOS)
- 基于Tokio的异步处理
- 简单易用的API
- 支持多次注册处理函数
使用方法
基本用法
use async_ctrlc::CtrlC;
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() {
// 创建Ctrl-C处理器
let ctrlc = CtrlC::new().expect("无法创建Ctrl-C处理器");
// 注册处理函数
let task = tokio::spawn(async move {
ctrlc.await;
println!("收到Ctrl-C信号,正在优雅关闭...");
});
println!("程序运行中,按Ctrl-C终止...");
// 模拟长时间运行的任务
loop {
println!("处理中...");
sleep(Duration::from_secs(1)).await;
}
// 等待Ctrl-C任务完成
task.await.unwrap();
}
多个处理函数
use async_ctrlc::CtrlC;
use tokio::sync::broadcast;
#[tokio::main]
async fn main() {
let ctrlc = CtrlC::new().unwrap();
let (shutdown_sender, _) = broadcast::channel(1);
// 任务1
let ctrlc1 = ctrlc.clone();
let sender1 = shutdown_sender.clone();
tokio::spawn(async move {
ctrlc1.await;
println!("处理程序1收到关闭信号");
sender1.send(()).unwrap();
});
// 任务2
let ctrlc2 = ctrlc.clone();
let sender2 = shutdown_sender.clone();
tokio::spawn(async move {
ctrlc2.await;
println!("处理程序2收到关闭信号");
sender2.send(()).unwrap();
});
// 主循环
let mut receiver = shutdown_sender.subscribe();
loop {
tokio::select! {
_ = receiver.recv() => {
println!("所有处理程序已完成,正在退出...");
break;
}
_ = tokio::time::sleep(Duration::from_secs(1)) => {
println!("主程序运行中...");
}
}
}
}
与Tokio信号处理结合
use async_ctrlc::CtrlC;
use tokio::signal::unix::{signal, SignalKind};
#[tokio::main]
async fn main() {
let ctrlc = CtrlC::new().unwrap();
let mut sigterm = signal(SignalKind::terminate()).unwrap();
tokio::select! {
_ = ctrlc => {
println!("收到Ctrl-C信号");
}
_ = sigterm.recv() => {
println!("收到SIGTERM信号");
}
}
println!("正在执行清理工作...");
tokio::time::sleep(Duration::from_secs(1)).await;
println!("程序已退出");
}
完整示例代码
下面是一个结合了上述所有功能的完整示例,展示了如何使用async-ctrlc处理Ctrl-C信号,同时结合其他系统信号和多个处理程序:
use async_ctrlc::CtrlC;
use tokio::{
signal::unix::{signal, SignalKind},
sync::broadcast,
time::{sleep, Duration},
};
#[tokio::main]
async fn main() {
// 创建Ctrl-C处理器
let ctrlc = CtrlC::new().expect("创建Ctrl-C处理器失败");
// 创建广播通道用于协调关闭
let (shutdown_sender, _) = broadcast::channel(1);
// 注册Ctrl-C处理程序1
let ctrlc1 = ctrlc.clone();
let sender1 = shutdown_sender.clone();
tokio::spawn(async move {
ctrlc1.await;
println!("[处理程序1] 收到Ctrl-C信号");
sender1.send(()).expect("发送关闭信号失败");
});
// 注册Ctrl-C处理程序2
let ctrlc2 = ctrlc.clone();
let sender2 = shutdown_sender.clone();
tokio::spawn(async move {
ctrlc2.await;
println!("[处理程序2] 收到Ctrl-C信号");
sender2.send(()).expect("发送关闭信号失败");
});
// 注册SIGTERM处理程序
let mut sigterm = signal(SignalKind::terminate()).expect("创建SIGTERM处理器失败");
let sender3 = shutdown_sender.clone();
tokio::spawn(async move {
sigterm.recv().await;
println!("[处理程序3] 收到SIGTERM信号");
sender3.send(()).expect("发送关闭信号失败");
});
println!("程序已启动,按Ctrl-C或发送SIGTERM信号终止...");
// 主循环
let mut receiver = shutdown_sender.subscribe();
loop {
tokio::select! {
_ = receiver.recv() => {
println!("收到关闭信号,开始清理...");
// 模拟清理工作
sleep(Duration::from_secs(2)).await;
println!("清理完成,程序退出");
break;
}
_ = sleep(Duration::from_secs(1)) => {
println!("主程序正常运行中...");
}
}
}
}
安装
在Cargo.toml中添加依赖:
[dependencies]
async-ctrlc = "1.2"
tokio = { version = "1.0", features = ["full"] }
注意事项
- 在Windows上,
async-ctrlc
使用控制台控制处理器,而在Unix系统上使用信号处理 - 多次调用
CtrlC::new()
会创建独立的新实例,每个实例都需要单独等待 - 在Tokio运行时中工作最佳,但也支持其他异步运行时
这个库为需要处理用户中断的Rust异步应用程序提供了一种简单而强大的解决方案,特别适合服务器、长时间运行的进程或需要执行清理工作的应用程序。