我目前正在学习rust,我想用以下元素制作一个小程序:
- 一个 spawner 任务,它创建其他 tokio 任务并将它们存储在 hashmap 中。
- 在创建任务之前,生成器会查看地图内部以检查任务是否已经在使用相同的 id 运行。在这种情况下,它会更改任务对象中的值,而不是创建新值并让它完成。
- 当一个任务完成时,它会将自己从 hashmap 中移除。
我在下面的代码中成功地实现了这个机制。但是,我想将任务封装在对象本身的“运行”函数中。当我这样做时,我很难编译它,因为我不清楚 run 方法中 self 参数的所有权。由于对象存储在 HashMap 中,因此我无法将其作为正确运行的 self 参数提供,因此我总是遇到编译问题。此外,我很难在任务和生成器之间创建正确的锁定机制,特别是因为我无法在不锁定整个哈希图的情况下更改任务对象的属性。
有人知道我如何实现这个吗?
use rand::{Rng};
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use tokio::time::sleep;
use tokio::time::Duration;
struct MyTask {
target: f32,
task_id: i32,
}
impl MyTask {
pub fn new(task_id: i32) -> Self {
return MyTask {
target: 0.0,
task_id: task_id,
};
}
pub fn set_target(&mut self, target: f32) {
self.target = target;
}
// pub async fn run(self) -> JoinHandle<()> {
// PUT THE TASK HERE
// }
}
fn create_task(task_id: i32, tasks: Arc<RwLock<HashMap<i32, MyTask>>>) {
let tasks = tasks.clone();
{
let mut lock = tasks.write().unwrap();
if !lock.contains_key(&task_id) {
let t = MyTask::new(task_id);
lock.insert(task_id, t);
} else {
return;
}
}
println!("spawn task {:?}", task_id);
// PUT THIS TASK IN run AND REPLACE cur_task by "self"
tokio::spawn(async move {
loop {
{
let mut tasks = tasks.write().unwrap();
let mut cur_task = tasks.get_mut(&task_id).unwrap();
if cur_task.target < 0.0 {
break;
}
cur_task.target = cur_task.target - 1.0;
println!("target of {:?} is {:?}", cur_task.task_id, cur_task.target);
}
sleep(Duration::from_millis(1000)).await;
}
println!("task {:?} finished", task_id);
{
let mut tasks = tasks.write().unwrap();
tasks.remove(&task_id);
println!("{:?} tasks running", tasks.len());
}
});
}
#[tokio::main]
async fn main() {
let tasks: Arc<RwLock<HashMap<i32, MyTask>>> = Arc::new(RwLock::new(HashMap::new()));
tokio::spawn(async move {
loop {
let task_id = rand::thread_rng().gen_range(0..10);
create_task(task_id, tasks.clone());
{
let mut tasks = tasks.write().unwrap();
if tasks.len() != 0 {
println!("{:?} tasks running", tasks.len());
let mut rand_idx = 1;
if tasks.len()>1 {
rand_idx = rand::thread_rng().gen_range(1..tasks.len());
}
let selected_id: i32;
selected_id = * tasks.keys().nth(rand_idx-1).unwrap();
let t = tasks.get_mut(&selected_id).unwrap();
t.set_target(rand::thread_rng().gen_range(0.0..10.0));
}
}
sleep(Duration::from_millis(2000)).await;
}
})
.await
.unwrap();
}