知用网
白蓝主题五 · 清爽阅读
首页  > 软件入门

线程同步机制:多线程抢资源时怎么不打架?

你写了个程序,开了两个线程一起往同一个变量里加数字,结果最后值不对——不是少加了,就是多加了。这不是代码写错了,是典型的“线程抢资源”问题。

为什么需要同步

想象食堂打饭:窗口只有一个,但七八个人同时挤过去递饭卡。没排队的话,可能刷了两次卡只打了一份饭,或者饭打了两份却只扣一次钱。线程也一样,CPU 切换飞快,两个线程几乎同时读、改、写一个变量,中间一插队,数据就乱了。

最常用的同步工具:锁(Lock)

就像给食堂窗口装个闸机,一次只放一个人进去。Java 里用 synchronized,Python 用 threading.Lock,C# 用 lock 关键字。

Python 示例:

import threading

counter = 0
lock = threading.Lock()

def add_one():
global counter
for _ in range(100000):
with lock:
counter += 1

t1 = threading.Thread(target=add_one)
t2 = threading.Thread(target=add_one)
t1.start()
t2.start()
t1.join()
t2.join()
print(counter) # 稳稳输出 200000

除了锁,还有啥?

锁太“霸道”,容易卡住别人。有些场景更适合轻量级方案:

  • 原子操作:像 Python 的 queue.Queue,内部已做好同步,直接 put() / get() 就行;
  • 信号量(Semaphore):控制最多几个线程能进某段代码,比如限制数据库连接池最多 5 个并发;
  • 条件变量(Condition):适合“等通知”场景,比如生产者造好数据,才通知消费者来取。

小心死锁

两个线程各拿一把锁,又都等着对方手里的另一把——谁也不松手,就僵住了。典型例子:线程 A 先锁住账户 X 再去锁 Y,线程 B 偏偏先锁 Y 再锁 X。解决办法很简单:所有线程按固定顺序拿锁,比如永远先 X 后 Y。

同步不是万能膏药。过度加锁会拖慢速度,该异步的别硬塞同步。关键是在“安全”和“效率”之间找个舒服的平衡点。