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

无其他线程执行release操作时,加锁是否无需添加acquire屏障?

当无其他线程执行解锁操作时,加锁是否可以省略acquire语义?

本问题是此前关于「单次自旋锁是否需要内存acquire屏障」问题的延伸。

常规锁的acquire/release语义示例

正常情况下,加锁时需要添加acquire语义,解锁时添加release语义以避免竞争,示例如下:

code block0

atomic_int lock = 0;

// 多线程执行
void threads(void)
{
    while (atomic_exchange_explicit(&lock, 1, memory_order_acquire)); // 加锁(acquire语义)
    // 临界区操作
    atomic_store_explicit(&lock, 0, memory_order_release); // 解锁(release语义)
}

无竞争场景下的relaxed加锁示例

在无竞争的场景下(比如仅单个线程执行相关代码),加锁时无需添加acquire语义,此处用try-lock指代该逻辑:

code block1

atomic_int lock = 0;

// 多线程执行
void threads(void)
{
    int v = 0;
    // try_lock,此处可用relaxed语义
    if (!atomic_compare_exchange_strong_explicit(&lock, &v, 1, memory_order_relaxed, memory_order_relaxed)) {
        return; // try_lock失败则返回
    }
    // 临界区操作
    // 永不解锁
}

核心问题:无外部release时,加锁是否需要acquire?

现在探讨核心问题:当没有其他线程执行解锁(release)操作时,加锁是否可以不添加acquire屏障?

抽象示例(code block2)

#define EXIT_FLAG 1
#define WORK_FLAG 2

atomic_int state = 0;

void thread0(void)
{
    int tmp;
    while (1) {
        tmp = 0;
        // 此处是否需要acquire语义?
        if (!atomic_compare_exchange_strong_explicit(&state, &tmp, WORK_FLAG, memory_order_relaxed, memory_order_relaxed)) {
            assert(tmp == EXIT_FLAG);
            return;
        }

        // 执行工作任务

        tmp = WORK_FLAG;
        // 此处必须用release语义,配合thread1的acquire
        if (!atomic_compare_exchange_strong_explicit(&state, &tmp, 0, memory_order_release, memory_order_relaxed)) {
            assert(tmp == (EXIT_FLAG | WORK_FLAG));
            // 执行清理操作
            return;
        }
    }
}

void thread1(void)
{
    int tmp = 0;

    while (1) {
        // 此处必须用acquire语义,配合thread0的release
        if (atomic_compare_exchange_strong_explicit(&state, &tmp, tmp | EXIT_FLAG, memory_order_acquire, memory_order_relaxed))
            break;
    }

    if (!(tmp & WORK_FLAG)) {
        // 执行清理操作
    }
}

我们需要避免工作任务(do work)与清理操作(do clean)产生竞争,以下是更具体的实现示例:

具体实现示例(code block3)

#define EXIT_FLAG 1
#define WORK_FLAG 2

atomic_int state = 0;
struct work_struct *work_data; // 用calloc初始化

void thread0(void)
{
    int tmp;
    while (1) {
        tmp = 0;
        // 此处是否需要acquire语义?
        if (!atomic_compare_exchange_strong_explicit(&state, &tmp, WORK_FLAG, memory_order_relaxed, memory_order_relaxed)) {
            assert(tmp == EXIT_FLAG);
            return;
        }

        read/write(*work_data); // 读写工作数据

        tmp = WORK_FLAG;
        // 此处必须用release语义,配合thread1的acquire
        if (!atomic_compare_exchange_strong_explicit(&state, &tmp, 0, memory_order_release, memory_order_relaxed)) {
            assert(tmp == (EXIT_FLAG | WORK_FLAG));
            free(work_data);
            work_data = NULL;
            return;
        }
    }
}

void thread1(void)
{
    int tmp = 0;

    while (1) {
        // 此处必须用acquire语义,配合thread0的release
        if (atomic_compare_exchange_strong_explicit(&state, &tmp, tmp | EXIT_FLAG, memory_order_acquire, memory_order_relaxed))
            break;
    }

    if (!(tmp & WORK_FLAG)) {
        free(work_data);
        work_data = NULL;
    }
}

逻辑抽象(code block4)

lock_t lock;

// 工作线程
void thread0(void)
{
    while (1) {
        if (!try_lock_relaxed(&lock)) // 此处是否需要acquire语义?我认为不需要
            return;
        // 执行工作任务
        unlock_release(&lock);  // 必须用release语义
    }
}

// 其他线程
void threads(void)
{
    // 此处必须用acquire语义,配合thread0的unlock_release
    if (!try_lock_acquire(&lock))
        return;
    // 执行清理或其他工作
    // 永不解锁
}

结论:不需要添加acquire语义

在thread0的场景下,完全不需要在加锁时添加acquire语义,理由如下:

  1. 线程内天然有序:thread0的加锁操作是承接自己上一次的解锁(release)操作,同一线程内的指令执行天然有顺序一致性,不需要acquire来约束内存可见性。
  2. acquire的核心作用不适用:acquire语义的核心是确保加锁后的操作能看到其他线程解锁前的所有修改,但此处thread0是唯一执行解锁操作的线程,不存在“其他线程的修改”需要同步的场景。
  3. 已有release语义保障:thread0上一次解锁的release语义已经确保了之前的工作任务操作完成并对后续可见,因此本次加锁后的工作任务不需要额外的acquire来保障内存可见性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 18:15:55