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

Rust中如何提前终止多个等待RwLock::write锁的线程任务

解决方案

核心思路

标准库的RwLock没有提供主动终止其他线程写锁等待的API,因此不需要尝试从锁层面终止等待,而是从逻辑上避免多余线程进入写锁等待队列,从根源解决问题。


方案1:原子标记实现单线程扩容(推荐)

新增两个原子状态变量控制扩容流程,确保同一时间最多只有1个线程会尝试申请写锁执行扩容,其余线程直接等待扩容完成即可,完全不需要触碰写锁:

共享状态定义

use std::sync::{RwLock, atomic::{AtomicBool, AtomicUsize}};

struct SharedStore {
    memmap: RwLock<Mmap>, // 现有共享memmap
    is_expanding: AtomicBool, // 标记是否有线程正在执行扩容
    memmap_version: AtomicUsize, // 每次扩容后版本号+1,用于判断扩容是否完成
    capacity: usize, // 当前存储容量
}

Worker线程逻辑调整

fn thread_worker(tasks: Vec<Task>, shared_store: &SharedStore) {
    let mut read_lock = shared_store.memmap.read().unwrap();
    let mut current_version = shared_store.memmap_version.load(Ordering::Acquire);
    
    for task in tasks {
        // 忽略空间检查的竞态条件,和原有逻辑一致
        if out_of_space(&read_lock, &task) {
            // 先释放读锁
            drop(read_lock);

            // CAS尝试抢占扩容权限,只有成功的线程会执行扩容
            if shared_store.is_expanding.compare_exchange(
                false, true,
                Ordering::AcqRel, Ordering::Acquire
            ).is_ok() {
                // 抢到扩容权限,申请写锁执行扩容
                let mut write_lock = shared_store.memmap.write().unwrap();
                // 双重检查,避免已经被其他线程扩容过
                if shared_store.memmap_version.load(Ordering::Acquire) == current_version {
                    // 执行扩容逻辑:销毁旧memmap、重新分配文件、创建新memmap
                    *write_lock = expand_mmap(write_lock.as_ref());
                    shared_store.capacity = new_capacity;
                    // 版本号+1,通知其他线程扩容完成
                    shared_store.memmap_version.fetch_add(1, Ordering::Release);
                }
                // 释放写锁
                drop(write_lock);
                // 标记扩容结束
                shared_store.is_expanding.store(false, Ordering::Release);
            } else {
                // 没抢到扩容权限,等待扩容完成即可,不需要申请写锁
                while shared_store.memmap_version.load(Ordering::Acquire) == current_version {
                    std::thread::yield_now();
                    // 也可以用条件变量等待,减少空转开销
                }
            }

            // 扩容完成,重新申请读锁继续处理
            read_lock = shared_store.memmap.read().unwrap();
            current_version = shared_store.memmap_version.load(Ordering::Acquire);
        }
        // 原有任务处理逻辑,直接操作read_lock对应的memmap
        process_task(&read_lock, task);
    }
    drop(read_lock);
}

该方案完全避免了多线程同时等待写锁的问题,仅会有一个线程进入写锁等待队列,不会出现你提到的写锁排队导致的读锁阻塞问题。


方案2:兼容现有逻辑的最小修改

如果不想新增原子状态,也可以在现有逻辑基础上增加扩容完成标记,避免无效等待:

  1. 新增原子AtomicBool类型的expand_finished,每次触发扩容前先设为false
  2. 第一个拿到写锁的线程完成扩容后将expand_finished设为true
  3. 其余线程拿到写锁的第一时间先检查expand_finished,如果为true直接释放写锁即可

该方案虽然还是会有多个线程进入写锁等待队列,但每个排队的线程拿到锁后会立即释放,不会造成长时间阻塞,仅产生可忽略的锁调度开销。


注意事项

如果使用的是写优先的RwLock实现(包括std默认实现、parking_lot的默认RwLock),只要有写锁处于等待队列,新的读锁申请就会被阻塞,因此方案1是最优选择,从根源避免多余写锁排队。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 22:54:03