【C/C++】多线程下自旋锁的行为逻辑

发布于:2025-05-28 ⋅ 阅读:(30) ⋅ 点赞:(0)

多线程下自旋锁的行为逻辑

如果是自旋锁(spinlock)逻辑,当一个线程已经持有锁时,其他线程会不断尝试获取锁,处于“忙等(busy-waiting)”状态,不会主动休眠、挂起或进入阻塞态,除非你显式地这么写。


1 自旋锁的基本逻辑

std::atomic_flag lock = ATOMIC_FLAG_INIT;

void lock_spin() {
    while (lock.test_and_set(std::memory_order_acquire)) {
        // 自旋:啥也不做,继续尝试获取锁
    }
}

void unlock_spin() {
    lock.clear(std::memory_order_release);
}
  • test_and_set() 会原子地设置 locktrue,并返回旧值。
  • 如果另一个线程已设置 lock = true,那么当前线程会进入 while 循环 —— 这就是“忙等”。
  • 自旋期间线程不会释放 CPU,也不会进入 sleep 或 yield,仍然在占用计算资源。

2 线程状态分析

线程状态 说明
持锁线程 正常执行,直到 unlock()
自旋线程 CPU 上循环尝试获取锁(高 CPU 占用),处于 Running 状态
阻塞线程 不是自旋锁的行为,阻塞/挂起是互斥锁或条件变量做的事

在操作系统中,自旋线程的状态在调度器眼中通常是 “running”,如果被抢占则为 “ready”,而不是 “waiting” 或 “sleeping”。


3 自旋锁缺点

  • CPU 浪费大:线程处于忙等状态,浪费 CPU 周期。
  • 在多核下高竞争时非常低效。
  • 如果持锁线程长时间执行(或阻塞),自旋线程将毫无意义地消耗资源。

4 自旋锁优化方式

  1. 加上 pause/yield 提示 CPU 优化自旋:
while (lock.test_and_set(std::memory_order_acquire)) {
    std::this_thread::yield();  // Hint 给操作系统让出 CPU
}
  1. 自旋 + 回退 + sleep(适合高争用):
int count = 0;
while (lock.test_and_set(std::memory_order_acquire)) {
    if (++count > 1000) {
        std::this_thread::sleep_for(std::chrono::microseconds(10));  // 主动退让
    }
}

5 多核场景

  1. 可以有多个线程处于“运行态”吗?

可以有多个线程处于“就绪态(ready)”或“运行态(running)”,但实际同时运行的线程数量受限于 CPU 核心数。

  • 操作系统的线程调度器会维护多个状态:

    • Running:正在某个 CPU 核上执行。
    • Ready:准备好运行,但暂时没分配到 CPU。
    • Waiting/Sleeping/Blocked:在等 I/O、锁、条件变量等。
  • 如果你的系统是 4 核 CPU,那么最多可以同时运行 4 个线程(真正的并行)。

  • 其他线程即使“处于运行态”,但本质上是 ready 状态,被 OS 调度器等待调度。

  1. CPU 怎么决定“分配给谁”?
    由操作系统的 线程调度器(scheduler) 决定。调度策略有多种:
策略 描述
时间片轮转(round-robin) 每个线程轮流使用 CPU
优先级调度 优先级高的线程更容易获得 CPU
多队列反馈 结合线程运行情况动态调整优先级

调度器会考虑:

  • 线程优先级(nice 值、real-time 等)
  • 当前负载(load balancing)
  • CPU cache affinity(避免 cache miss)
  • 多核负载均衡(尽量让线程平均分布到各个核心)
  1. 多核 CPU 下的多线程运行模型
  • 多核 = 真正的“并行”执行

如果你有 8 核 CPU,你的 8 个线程可以真正在同一时间点并行执行,每个线程占用一个核心。线程之间通过共享内存、缓存一致性协议(如 MESI)等机制保持同步。

示例:4 个线程,4 核 CPU

时间 核心 0 核心 1 核心 2 核心 3
T0 线程 A 线程 B 线程 C 线程 D
T1 线程 A 线程 B 线程 E 线程 F

同时最多执行 4 个线程,剩下的在线程队列中等待调度。

  1. 自旋锁下,多线程抢锁是怎样的?
  • 每个核心上的线程在同时执行 while (lock.test_and_set()),此时所有核的线程都在自旋。
  • 哪个线程在某一时刻成功把 lock 从 false 设置为 true,它就赢得了执行权,进入临界区。
  • 其他线程仍然自旋,不会被挂起。
  1. 举个真实案例:8线程自旋 + 4核 CPU

  2. 线程 A 拿到自旋锁。

  3. 线程 B~H 也在尝试拿锁,它们会在各自 CPU 核或被调度时执行 while 自旋。

  4. 一旦线程 A 执行完 unlock(),调度器从剩下的线程中挑选一个抢锁成功。

  5. 其余线程继续自旋。


总结

情况 自旋锁表现
锁未被持有 线程获取锁,正常执行
锁被持有 自旋线程持续运行,占用 CPU
高并发场景 多线程争用,自旋浪费严重
最佳用途 锁持有时间非常短的临界区,如 CPU 缓存级并发控制
问题 回答
多个线程可以运行吗? 可以,但并发度受限于 CPU 核数
谁获得 CPU 执行? 操作系统调度器决定,基于策略
多核是怎么处理多线程的? 各核独立运行线程,实现真正并行
自旋锁时线程状态? 在运行或就绪态,不会自动 sleep

网站公告

今日签到

点亮在社区的每一天
去签到