rust - 将 tokio::task::JoinHandle 存储在 HashMap 中并从另一个任务中访问它
问题描述
问候Rustaceans,
我目前正在尝试使用serenity-rs移植一个 JVM 应用程序(准确地说是一个不和谐的机器人)来生锈以获得乐趣和教育,而我目前正在用 tokio 任务和共享状态碰壁。我对整个生锈体验很陌生,所以请保持温和。
基本思想是,某些任务异步启动,并在等待一段时间后将一些数据插入共享(并发)映射中。程序的另一部分是监听事件,如果在第一个任务还在等待的时候发生了一个这样的事件,它将取消该任务,从而导致数据不会被插入到共享映射中。
我从一个“简化”示例开始,它看起来像这样,并且只依赖于 tokio。
[dependencies]
tokio = { version = "1", features = ["full"] }
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
use tokio::join;
use tokio::sync::RwLock;
use tokio::time;
#[tokio::main]
async fn main() {
let mut data: Arc<RwLock<HashMap<_,_>>> =
Arc::new(RwLock::new(HashMap::new()));
let mut tasks: Arc<RwLock<HashMap<String, _>>> =
Arc::new(RwLock::new(HashMap::new()));
let mut d1 = data.clone();
let handle_a = tokio::spawn(async move {
time::sleep(Duration::from_secs(30)).await;
{
let mut lock = d1.write().await;
lock.insert("foo", "bar");
}
});
let handle_a = Arc::new(handle_a);
{
let mut map = tasks.write().await;
map.insert("the_task".to_string(), handle_a.clone());
}
let mut d2 = data.clone();
let handle_b = tokio::spawn(async move {
tokio::time::sleep(Duration::from_secs(10)).await;
{
let d = d2.read().await;
let res = d.get("foo");
println!("After 10sec: {}", res.unwrap_or(&"None"));
let mut m = tasks.write().await;
{
let t = m.get("the_task").unwrap();
println!("Now cancelling: {:?}", t);
t.abort();
let _ = m.remove("the_task");
}
}
tokio::time::sleep(Duration::from_secs(25)).await;
{
let d = d2.read().await;
let res = d.get("foo").unwrap_or(&"None");
println!("After 35sec: {}", res)
}
});
join!(handle_a, handle_b);
}
当前编译时出现以下错误:
55 | join!(handle_a, handle_b);
| ^^^^^^^^ `Arc<tokio::task::JoinHandle<()>>` is not a future
我已经包装handle_a
了一个,Arc
因为我想在“主函数的末尾”等待它,同时仍然能够将对它的引用放入任务映射中。所以这显然似乎是错误的,但我想不出另一种方式来处理这个问题。简单地调用deref()
会handle_a
给出一个不同的错误:
`Future` is implemented for `&mut tokio::task::JoinHandle<()>`, but not for `&tokio::task::JoinHandle<()>
我想这是有道理的,因为Arc
状态的文档:
默认情况下,Rust 中的共享引用不允许突变,Arc 也不例外:您通常无法获得对 Arc 中某些内容的可变引用。
我认为 Rust 中期货的“基于拉取”的方法真正让我在这里挣扎,因为我需要一个对 JoinHandle 的引用以在 main 函数的末尾等待它。
可能这只是解决这个问题的完全错误的方法,所以我会非常感谢任何朝着正确方向的提示或轻推。
如果你读到这里,已经感谢你的时间了!
编辑:修复了接受的答案中提到的错字。
解决方案
tokio::spawn
已经在后台执行未来。如果你想等待它的结果,你只需要等待它,在这种情况下你不需要。您可以简单地将其替换为b.await
.
您在任务名称中还有一个小的逻辑错误 - 您使用"thetask"
而不是"the_task"
在一个查询中。我已经清理了一些代码,你可以在这个Playground上找到工作版本。
我不熟悉 Discord 机器人的设计,但如果你只有一个一直在运行的“事件循环”,负责创建和取消任务,你不需要为它创建一个任务tokio::spawn
但可以直接在main
函数中运行它。这意味着它能够tasks
独占地图,并且您不会遇到共享所有权的问题。
推荐阅读
- ubuntu - 文件描述符 3(管道:[34331])在 vgs 调用时泄露。父 PID 5424:/usr/sbin/grub-probe
- python - 如何从 dir() 的变量中获取值
- spring - 添加 Springs LDAP-Core 后终止
- typescript - 在 Typescript 中使用枚举作为泛型类型时不兼容的类型
- mysql - 如何在 Sequelize 模型中动态选择数据库(或“模式”)?
- puppet - Puppet 有条件地连接列表
- python - Python平衡括号错误与索引和缩进
- c++ - 在 C++ 中从 Base 对象转换为 Derived 对象
- javascript - 澄清作用域变量在 Javascript 闭包的上下文中的行为方式
- visual-studio-code - 调试时未部署VS代码扩展依赖项?