返回

锁的底层原理 — 25行代码轻松理解

IOS

导读

在计算机科学中,锁是一种同步机制,用于防止多个线程同时访问共享资源。本文将深入探讨锁的底层原理,用 25 行代码解析其工作机制。

何为锁?

锁是一种数据结构,它可以控制对共享资源的访问。当一个线程获取锁时,它可以独占访问共享资源,而其他线程必须等待锁释放。

锁的种类

锁有多种类型,每种类型都有其独特的特性:

  • 互斥锁:允许一次只有一个线程持有锁。
  • 读写锁:允许多个线程同时读取共享资源,但一次只有一个线程可以写入。
  • 自旋锁:当锁不可用时,线程会不断轮询锁,而不是进入休眠状态。

锁的底层实现

锁的底层实现通常使用原子操作,如“测试并设置”指令。这种指令允许线程检查锁的状态并原子地将其设置为已锁定。

原子操作

原子操作是一组指令,保证在执行过程中不会被中断。这对于锁的正确工作至关重要,因为多个线程可能同时尝试获取锁。

测试并设置指令

测试并设置指令(TAS)是一个原子操作,它检查锁的状态并将其设置为已锁定。其伪代码如下:

while (lock == 0) {
  if (TAS(&lock, 1)) {
    // 获取锁成功
    break;
  }
}

释放锁

释放锁的过程也很简单,它涉及将锁的状态设置为未锁定。

lock = 0;

锁在多线程中的作用

在多线程环境中,锁用于防止线程间的数据竞争。例如,考虑一个银行账户,其中多个线程同时尝试提取资金。如果没有锁,多个线程可能会同时修改账户余额,导致不一致的数据。

使用锁的示例

以下是一个简单的 C++ 代码示例,演示了锁的使用:

#include <thread>
#include <mutex>

std::mutex m;
int counter = 0;

void increment_counter() {
  std::lock_guard<std::mutex> guard(m);  // 获取锁
  counter++;                               // 修改共享资源
}

int main() {
  std::thread t1(increment_counter);
  std::thread t2(increment_counter);

  t1.join();
  t2.join();

  std::cout << "Counter: " << counter << std::endl;  // 打印结果
  return 0;
}

总结

锁是多线程编程中必不可少的同步机制。理解锁的底层原理至关重要,因为它可以帮助我们编写安全高效的并发代码。