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

基于原子基元实现类RWLock基础结构的问题咨询

偏向写操作的读写锁实现问题分析与修复

原需求梳理

  • 支持高读线程竞争,偏向写操作:写线程发起后,阻塞所有后续读线程直至写操作完成
  • 环境限制:无内存分配器,仅允许使用栈变量与静态变量
  • 特殊逻辑:已有写线程在修复数据结构时,新写线程无需执行任何操作

原实现的核心缺陷

  1. 写线程等待条件逻辑错误
    原代码通过active_thread_counter.fetch_sub(writer_sign_num)标记写请求,随后等待计数器等于-writer_sign_num。但实际发起写请求时,若存在N个活跃读线程,计数器会变为N - writer_sign_num,而非-writer_sign_num。这会导致写线程必须等待所有读线程完全退出才能继续,虽符合“等待已有读线程完成”的逻辑,但计数器设计极易引发竞态与误解。

  2. 多写线程竞态导致无效写操作
    原代码仅判断发起写请求时是否已有写线程等待,但当一个写线程完成修复后,计数器回到0,后续写线程会重新发起写请求——即使数据结构已无需修复,违背了“已有写线程修复后新写线程无需执行”的核心需求。

  3. 读线程数量溢出风险
    writer_sign_num取值为0x100_0000_0000,若活跃读线程数量超过i64::MAX - writer_sign_num,计数器会溢出为负数,导致读线程被错误阻塞。

优化后的实现方案

基于原需求与环境限制,采用单原子变量+位标记的设计,逻辑清晰且无溢出风险:

use std::sync::atomic::{AtomicI64, Ordering};
use std::time::Duration;

// 静态原子锁:最高位标记写请求,低63位记录活跃读线程数
static LOCK: AtomicI64 = AtomicI64::new(0);
const WRITE_FLAG: i64 = 1 << 63; // i64最高位作为写请求标记

unsafe fn write() {
    // 尝试设置写请求标记,若已存在则直接返回(已有写线程在修复)
    let prev = LOCK.fetch_or(WRITE_FLAG, Ordering::SeqCst);
    if (prev & WRITE_FLAG) != 0 {
        return;
    }

    // 等待所有活跃读线程退出(低63位变为0)
    while (LOCK.load(Ordering::SeqCst) & !WRITE_FLAG) != 0 {
        std::thread::sleep(Duration::new(0, 1000));
    }

    // --------------------------
    // 此处执行数据结构修复操作
    // --------------------------

    // 清除写请求标记,允许后续读线程进入
    LOCK.fetch_and(!WRITE_FLAG, Ordering::SeqCst);
}

unsafe fn sign_in() {
    loop {
        let current = LOCK.load(Ordering::SeqCst);
        // 若存在写请求,等待
        if (current & WRITE_FLAG) != 0 {
            std::thread::sleep(Duration::from_nanos(2550));
            continue;
        }

        // 尝试增加读线程计数
        let prev = LOCK.fetch_add(1, Ordering::SeqCst);
        // 若增加计数前已出现写请求,撤销操作并等待
        if (prev & WRITE_FLAG) != 0 {
            LOCK.fetch_sub(1, Ordering::SeqCst);
            std::thread::sleep(Duration::from_nanos(2550));
            continue;
        }

        // 成功进入读操作
        return;
    }
}

unsafe fn sign_out() {
    // 减少读线程计数
    LOCK.fetch_sub(1, Ordering::SeqCst);
}

方案优势

  1. 偏向写的语义明确:写线程设置标记后,后续读线程立即阻塞,符合需求
  2. 多写线程逻辑正确:仅第一个设置写标记的线程会执行修复,其他线程直接返回
  3. 无溢出风险:低63位可支持最多2^63-1个活跃读线程,完全覆盖高读竞争场景
  4. 符合环境限制:仅使用静态原子变量,无需内存分配

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 14:24:56