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

Rust中如何非轮询式自动更新BTreeMap内过期项的live字段?

无需轮询的Beacon到期自动更新方案

你不用靠固定间隔轮询来更新live字段,下面两个方案能更高效地实现到期自动更新:

方案一:异步定时器(基于Tokio)

如果你的项目用异步框架(比如Tokio),可以给每个Beacon单独设置到期定时器,到点就修改live字段。

实现步骤:

  1. 把beacons改成线程安全的结构,因为异步任务需要跨线程访问它:
use std::sync::Arc;
use tokio::sync::Mutex;

struct ExampleStruct {
    // 其他字段...
    pub(crate) beacons: Arc<Mutex<BTreeMap<OwnedUserId, BeaconInfoEventContent>>>,
}
  1. 新增Beacon时,启动一个延迟任务,到期后更新live:
impl ExampleStruct {
    pub async fn add_beacon(&self, user_id: OwnedUserId, content: BeaconInfoEventContent) {
        // 先把Beacon存入Map
        let mut beacons = self.beacons.lock().await;
        beacons.insert(user_id.clone(), content.clone());
        drop(beacons); // 提前释放锁,避免持有锁等待定时器

        // 计算到期时间
        let expire_time = content.ts.to_system_time().unwrap() + content.timeout;
        let beacons_clone = self.beacons.clone();
        let user_id_clone = user_id.clone();

        // 启动到期任务
        tokio::spawn(async move {
            tokio::time::sleep_until(expire_time.into()).await;
            let mut beacons = beacons_clone.lock().await;
            if let Some(beacon) = beacons.get_mut(&user_id_clone) {
                // 只在当前live为true时更新,避免重复操作
                if beacon.live {
                    beacon.live = false;
                }
            }
        });
    }
}
  1. 如果需要提前删除Beacon,可以用AbortHandle取消对应的定时器任务,避免无效操作:
// 修改add_beacon,返回AbortHandle
pub async fn add_beacon(&self, user_id: OwnedUserId, content: BeaconInfoEventContent) -> tokio::task::AbortHandle {
    // ...前面的代码不变
    let task = tokio::spawn(async move { /* ... */ });
    task.abort_handle()
}

// 删除Beacon时取消任务
pub async fn remove_beacon(&self, user_id: &OwnedUserId, abort_handle: tokio::task::AbortHandle) {
    abort_handle.abort();
    let mut beacons = self.beacons.lock().await;
    beacons.remove(user_id);
}

方案二:优先级队列+单线程处理

如果是同步项目,或者不想创建大量异步任务,可以用优先级队列维护所有Beacon的到期时间,用一个单独线程专门处理到期事件。

实现步骤:

  1. 定义到期条目结构,用BinaryHeap实现最小堆(堆顶是最早到期的时间):
use std::collections::{BTreeMap, BinaryHeap};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::{Duration, SystemTime};

// 用Reverse包装,让BinaryHeap变成最小堆
#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct ExpireEntry {
    expire_time: SystemTime,
    user_id: OwnedUserId,
}

struct ExampleStruct {
    beacons: Arc<Mutex<BTreeMap<OwnedUserId, BeaconInfoEventContent>>>,
    expire_queue: Arc<Mutex<BinaryHeap<std::cmp::Reverse<ExpireEntry>>>>,
}
  1. 初始化时启动处理线程,循环处理到期事件:
impl ExampleStruct {
    pub fn new() -> Self {
        let beacons = Arc::new(Mutex::new(BTreeMap::new()));
        let expire_queue = Arc::new(Mutex::new(BinaryHeap::new()));
        
        // 启动处理线程
        let beacons_clone = beacons.clone();
        let expire_queue_clone = expire_queue.clone();
        thread::spawn(move || {
            loop {
                let next_entry = {
                    let mut queue = expire_queue_clone.lock().unwrap();
                    queue.pop().map(|rev| rev.0)
                };

                match next_entry {
                    Some(entry) => {
                        let now = SystemTime::now();
                        // 计算到到期时间的休眠时长,没到期就休眠
                        if let Some(sleep_duration) = entry.expire_time.duration_since(now).ok() {
                            thread::sleep(sleep_duration);
                        }

                        // 更新Beacon的live状态
                        let mut beacons = beacons_clone.lock().unwrap();
                        if let Some(beacon) = beacons.get_mut(&entry.user_id) {
                            if beacon.live {
                                beacon.live = false;
                            }
                        }
                    }
                    None => {
                        // 队列为空时,休眠一分钟再检查
                        thread::sleep(Duration::from_secs(60));
                    }
                }
            }
        });

        Self { beacons, expire_queue }
    }

    // 添加Beacon时,同时存入Map和优先级队列
    pub fn add_beacon(&self, user_id: OwnedUserId, content: BeaconInfoEventContent) {
        let expire_time = content.ts.to_system_time().unwrap() + content.timeout;
        let entry = ExpireEntry { expire_time, user_id: user_id.clone() };

        let mut beacons = self.beacons.lock().unwrap();
        beacons.insert(user_id, content);
        drop(beacons);

        let mut queue = self.expire_queue.lock().unwrap();
        queue.push(std::cmp::Reverse(entry));
    }
}

方案对比

  • 异步定时器:适合异步项目,任务独立,取消方便,Tokio的调度能高效处理大量任务,但Beacon数量极多时会创建大量异步任务(不过一般场景下没问题)。
  • 优先级队列:适合同步项目,集中处理到期事件,资源占用更可控,但需要处理队列中的无效条目(比如已删除的Beacon),队列为空时需要休眠等待。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 06:54:55