死锁

复制代码
 1 import threading
 2 
 3 
 4 # 全局变量
 5 g_num = 0
 6 
 7 
 8 # 对g_num进行加操作
 9 def sum_num1():
10     # 上锁
11     print("sun_num1...")
12     print(f"sum_num1 id(mutex) {id(mutex)}")
13     mutex.acquire()
14 
15     for i in range(1000000):
16         global g_num
17         g_num += 1
18 
19     print("g_num1:", g_num)
20 
21 
22 # 对g_num进行加操作
23 def sum_num2():
24     # 上锁
25     print("sun_num2...")
26     print(f"sum_num2 id(mutex) {id(mutex)}")
27     mutex.acquire()  # 没有释放
28 
29     for i in range(1000000):
30         global g_num
31         g_num += 1
32 
33     print("g_num2:", g_num)
34 
35 
36 if __name__ == '__main__':
37     # 创建锁
38     mutex = threading.Lock()  # 全局变量
39     print(id(mutex))
40     # 创建子线程
41     sum1_thread = threading.Thread(target=sum_num1)
42     sum2_thread = threading.Thread(target=sum_num2)
43 
44     # 启动线程
45     sum1_thread.start()
46     sum2_thread.start()

复制代码

输出:

2590825403712
sun_num1...
sum_num1 id(mutex) 2590825403712
sun_num2...
sum_num2 id(mutex) 2590825403712
g_num1: 1000000

从输出结果上看是同一把锁。锁主了。

2个线程都争取上锁(只能1个成功),成功后没有释放,导致另外一个线程一直等待(拿到锁的线程已经执行完了还是在等待)

总结

死锁发生的条件通常包括以下四个必要条件:

  1. 互斥条件(Mutual Exclusion):一个资源同时只能被一个线程或进程占用。
  2. 请求和保持条件(Hold and Wait):一个线程或进程在持有某个资源的同时,还请求其他资源。
  3. 不可剥夺条件(No Preemption):一个资源不能被强制从一个线程或进程中释放,只能由持有该资源的线程或进程主动释放。
  4. 循环等待条件(Circular Wait):多个线程或进程形成一个循环等待其他线程或进程所持有的资源。

避免死锁的最佳实践包括:

  1. 避免使用过多的锁。尽量简化并发代码,减少需要加锁的地方。
  2. 使用适当的锁粒度。将锁的范围控制在最小必要范围内,以降低死锁的概率。
  3. 避免嵌套锁。如果一个线程已经持有了一个锁,在持有的锁范围内尽量不要再去请求其他锁。
  4. 使用定时锁(Timeout Locks)。在加锁时设置超时时间,避免线程永久等待锁的释放。
  5. 使用死锁检测和恢复机制。可以周期性地检测死锁的发生,并采取相应的措施进行恢复,例如强制释放某些资源或回滚操作。
posted @   Allen_Hao  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能
点击右上角即可分享
微信分享提示