写过多线程程序的人都知道,多个线程同时操作同一个资源时,很容易出问题。比如两个线程同时往一个变量加1,结果可能只加了一次。这就是典型的“竞态条件”。要解决这个问题,就得靠线程锁机制。
为什么需要线程锁
想象一下你和室友共用一个冰箱,你们俩同时发现里面没牛奶了,于是都决定去买。结果就是,两个人各带一盒回来,家里多了盒多余的牛奶。在程序里,这种“重复操作”可能导致数据错乱、资源浪费,甚至系统崩溃。
线程锁的作用就是让某个代码段在同一时间只能被一个线程执行,其他线程得等着,等锁释放了才能进来。就像你去厨房做饭,先锁上门,别人只能等你做完出来再进。
常见的锁类型和使用方式
以 Python 为例,最常用的锁是 threading 模块里的 Lock:
import threading
import time
# 创建一个锁对象
counter_lock = threading.Lock()
shared_counter = 0
def worker():
global shared_counter
for _ in range(100000):
counter_lock.acquire() # 加锁
shared_counter += 1 # 操作共享资源
counter_lock.release() # 释放锁
# 启动两个线程
t1 = threading.Thread(target=worker)
t2 = threading.Thread(target=worker)
t1.start()
t2.start()
t1.join()
t2.join()
print(shared_counter) # 输出 200000,不会出错
上面这段代码中,每次修改 shared_counter 前都先 acquire 锁,改完后再 release。这样就能保证任何时候只有一个线程在改这个变量。
更简洁的写法是用 with 语句,自动加锁和释放:
def worker():
global shared_counter
for _ in range(100000):
with counter_lock:
shared_counter += 1
这种写法不容易忘记释放锁,推荐日常使用。
注意死锁问题
锁虽然好用,但用不好会“卡住”。比如两个线程各自拿着一把锁,又想拿对方的,结果谁也动不了,这就叫死锁。
避免死锁的关键是:统一加锁顺序。比如多个锁时,大家都按 A→B→C 的顺序来申请,就不会互相堵住。
其他类型的锁
除了基本的 Lock,还有 RLock(可重入锁),允许同一线程多次获取同一个锁,适合递归调用场景。还有 Semaphore 控制并发数量,像限制同时最多3个线程访问某资源。
实际开发中,比如做抢票系统、库存扣减、日志写入,都会用到锁机制。只要涉及共享数据,就得考虑线程安全。
别以为只有Python才要管这个,Java 的 synchronized、Go 的 sync.Mutex、C++ 的 std::mutex,原理都差不多。掌握一种,其他语言迁移起来很快。