You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

异步任务间获取JoinHandle遇阻:Arc使用与编译错误排查

异步任务监控方案的问题与解决

问题背景

我需要实现一套逻辑:生成多个无限循环的异步任务,同时通过独立监控任务追踪这些任务的运行状态。最初方案是将每个任务的JoinHandle存入以字符串为键的HashMap,再让监控任务循环调用is_finished()检查状态。但遇到以下问题:

  • 不克隆HashMap无法传递给监控任务,又担心克隆会复制任务句柄导致无法引用原任务,于是尝试用Arc智能指针,但实现出现编译错误。

错误代码与报错信息

type TasksMap = HashMap<String, Arc<JoinHandle<()>>>;

pub async fn monitor_each(db: &DbClient) -> Result<()> {
    let orderbooks = db::get_orderbooks().await?;
    
    for (address, _) in orderbooks {
        // do some stuff

        let ob_monitor_task = orderbook.clone().monitor();
        
        // Add the OBs to a map of tasks, so we can monitor if they're running
        tasks
            .entry(orderbook.addr.to_string())
            .or_insert(ob_monitor_task);
    }
    Ok(())
}

/// Periodically check status of tasks
async fn check_tasks_status(tasks: &TasksMap) -> Result<()> {
    tokio::spawn(async move {
        loop {
            for (ob_address, task) in tasks.iter() {
                let og_task = Arc::get_mut(task).unwrap(); // 编译错误位置
                if og_task.is_finished() {
                    error!("Monitor task not running for OB with address {ob_address}");
                }
            }
            tokio::time::sleep(Duration::from_secs(10)).await;
        }
    })
    .await?
}

impl Orderbook {
    /// Starts a task that monitors periodically the orderbook.
    pub fn monitor(&mut self) -> Arc<JoinHandle<()>> {
        let handle = tokio::spawn(async move {
            loop {
                // do some stuff

                tokio::time::sleep(Duration::from_secs(5)).await;
            }
        });

        Arc::new(handle)
    }
}

编译错误:

Note: expected mutable reference `&mut Arc<_>` found reference `&Arc<tokio::task::JoinHandle<()>>`

同时我不确定从Arc中获取的og_task是否为原任务的句柄。


解决方案

错误根源

  1. Arc::get_mut要求传入可变引用(&mut Arc<T>),但tasks.iter()返回的是不可变引用(&Arc<JoinHandle<()>>),类型不匹配导致报错。
  2. 完全没必要使用Arc::get_mut:JoinHandle::is_finished()是不可变方法,直接通过Arc的不可变引用即可调用。

修正后的代码

调整监控任务函数

/// Periodically check status of tasks
async fn check_tasks_status(tasks: Arc<TasksMap>) -> Result<()> {
    tokio::spawn(async move {
        loop {
            for (ob_address, task) in tasks.iter() {
                // 直接通过Arc的不可变引用调用is_finished,自动解引用到JoinHandle
                if task.is_finished() {
                    error!("Monitor task not running for OB with address {ob_address}");
                }
            }
            tokio::time::sleep(Duration::from_secs(10)).await;
        }
    })
    .await?
}

调整任务创建与Arc传递逻辑

pub async fn monitor_each(db: &DbClient) -> Result<()> {
    // 用Arc包裹HashMap,方便多任务共享
    let mut tasks = Arc::new(HashMap::new());
    let orderbooks = db::get_orderbooks().await?;
    
    for (address, _) in orderbooks {
        // 假设此处获取或初始化Orderbook实例
        let mut orderbook = ...;
        let ob_monitor_task = orderbook.monitor();
        
        // 使用Arc::make_mut获取内部HashMap的可变引用,仅当唯一引用时直接修改,否则克隆(初始化阶段不会触发克隆)
        let mut tasks_mut = Arc::make_mut(&mut tasks);
        tasks_mut
            .entry(orderbook.addr.to_string())
            .or_insert(ob_monitor_task);
    }
    
    // 克隆Arc传递给监控任务,仅增加引用计数,不会复制任务句柄或HashMap
    check_tasks_status(tasks.clone()).await?;
    Ok(())
}

关键说明

  • Arc的作用:克隆Arc只会增加引用计数,不会复制内部的HashMap或JoinHandle,所有共享的Arc都指向同一个原任务句柄,监控任务能正确获取原任务状态。
  • JoinHandle的访问:Arc实现了Deref trait,会自动解引用到内部的JoinHandle,因此可以直接通过task.is_finished()调用方法,无需额外转换。
  • 修改共享HashMap:初始化阶段用Arc::make_mut获取可变引用,确保安全修改共享的HashMap,后续监控任务仅做只读操作,无需可变权限。

内容的提问来源于stack exchange,提问作者Milkncookiez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 14:23:21