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

基于std::atomic实现互斥锁的相关技术问题咨询

线程安全锁实现疑问解答
class AtomicLock
{
    std::atomic_flag flag;

public:

    void lock() {
        while (flag.test_and_set(std::memory_order_acquire))
            flag.wait(true, std::memory_order_acquire);
    }

    void unlock() {
        flag.clear(std::memory_order_release);
        flag.notify_one();
    }
};

class ThreadAtomicLock
{
    static std::atomic_long id_generator;
    std::atomic<long> owner_thread = -1;

    static long get_id() {
        thread_local auto id = id_generator++;
        return id;
    }

public:

    void lock() {
        long expected = -1;
        while (!owner_thread.compare_exchange_weak(expected, get_id(), std::memory_order_acquire)) {
            owner_thread.wait(expected, std::memory_order_acquire);
            expected = -1;
        }
    }

    void unlock() {
        long expected = get_id();
        assert (
            owner_thread.compare_exchange_strong(expected, -1, std::memory_order_release) &&
            "unlock of unowned lock"
        );
        owner_thread.notify_one();
    }
};

std::atomic_long ThreadAtomicLock::id_generator = 0;

void testAtomicLock() {
    std::vector<std::thread> threads;
    std::atomic<bool> flag = false;
    // AtomicLock lock;
    ThreadAtomicLock lock;
    for (int i = 0; i < 100; i++) {
        threads.emplace_back([&] {
            while (!flag.load(std::memory_order_acquire));
            lock.lock();
            std::cout << "Hello";
            std::cout << " from thread ";
            std::cout << std::this_thread::get_id() << " ";
            int n = 10;
            while (n--) std::cout << "=";
            std::cout << "\n";
            lock.unlock();
        });
    }
    flag.store(true, std::memory_order_release);
    for (auto& t : threads) t.join();
}

上述AtomicLock类可正常运行,但存在任意线程均可解锁非自身持有的锁的问题。为此实现了ThreadAtomicLock类,确保仅持有锁的线程才能执行解锁操作,针对该实现有以下疑问及解答:


1. 该实现是否完整正确?能否放宽约束?

这个实现基本正确,满足互斥性、所有权校验、内存可见性等锁的核心要求,但有可优化或需注意的点:

  • 正确性:通过acquire/release内存序保证了临界区操作的跨线程可见性,所有权校验逻辑能阻止非持有线程解锁,互斥逻辑也能保证同一时间只有一个线程进入临界区。
  • 可调整的约束:
    • lock()中每次wait后重置expected=-1的逻辑可以保留,若想优化可利用compare_exchange_weak失败后更新的expected值,但当前逻辑更清晰。
    • 内存序不能随意放宽:lock()的acquire和unlock()的release是保证内存同步的关键,必须保留。
    • 潜在缺失:未支持递归加锁,若同一线程多次调用lock()会触发死锁,若需要该特性,可添加线程本地计数器记录加锁次数。

2. 解锁函数中的compare_exchange_strong能否替换为compare_exchange_weak?

不建议替换:

  • compare_exchange_weak存在伪失败可能(CPU层面的虚假失败,即使值匹配也可能返回false),而unlock()是必须保证原子性的操作——只有当前线程持有锁时,才能将owner_thread置为-1。
  • 伪失败会导致断言触发(即使线程合法持有锁),引发无意义的错误。compare_exchange_strong能保证只要值匹配就一定会成功,适合这种必须一次性完成的场景。

3. 线程ID生成机制是否有更优实现?

当前方案已经高效,可考虑两种替代方向:

  • 复用std::thread::id:通过std::hash<std::thread::id>{}(std::this_thread::get_id())将系统线程ID转换为整数,无需额外原子生成操作。但要注意哈希值存在极低的冲突概率,对唯一性要求极高时原方案更稳妥。
  • 使用平台原生线程ID:比如Linux的gettid()、Windows的GetCurrentThreadId(),这类ID是系统级唯一值,无需自行生成,但会引入平台依赖,跨平台场景下不适用。

4. 锁定函数中的wait调用能否使用memory_order_relaxed?

不能:

  • wait的内存序参数决定了唤醒后对原子变量的加载语义。当线程被notify_one唤醒时,需要确保后续的compare_exchange_weak能看到其他线程释放锁时的owner_thread更新(从线程ID变为-1)。
  • 若使用memory_order_relaxed,无法保证这种内存可见性,可能导致线程唤醒后仍读取旧值,进入无效循环甚至触发逻辑错误,必须保留memory_order_acquire来保证同步。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 23:42:06