GIL全局解释锁

  1. GIL本质上是一个互斥锁。

  2. GIL是为了阻止同一个进程内多个进程同时执行(并行)

    • 单个进程下的多个线程无法实现并行,但能实现并发
  3. 这把锁主要是因为Cpython的内存管理不是线程安全的

    • 保证线程在执行任务时不会被垃圾回收机制回收
  1. from threading import Thread
  2. import time
  3. num = 100
  4. def task():
  5. global num
  6. num2 = num
  7. time.sleep(1)
  8. num = num2 - 1
  9. print(num)
  10. for line in range(100):
  11. t = Thread(target=task)
  12. t.start()
  13. # 这里的运行结果都是99, 加了IO操作,所有线程都对num进行了减值操作,由于GIL锁的存在,没有修改成功,都是99

多线程的作用

  1. 计算密集型, 有四个任务,每个任务需要10s

单核:

  • 开启进程

    • 消耗资源过大
    • 4个进程: 40s
  • 开启线程
    • 消耗资源远小于进程
    • 4个线程: 40s

多核:

  • 开启进程

    • 并行执行, 效率比较高
    • 4个进程: 10s
  • 开启线程
    • 并发执行,执行效率低
    • 4个线程: 40s
  1. IO密集型, 四个任务, 每个任务需要10s

单核:

  • 开启进程

    • 消耗资源过大
    • 4个进程: 40s
  • 开启线程
    • 消耗资源远小于进程
    • 4个线程: 40s

多核:

  • 开启进程

    • 并行执行, 效率小于多线程, 但是遇到IO会立马切换CPU的执行权限
    • 4个进程: 40s + 开启进程消耗的额外时间
  • 开启线程
    • 并发执行,执行效率高于多进程
    • 4个线程: 40s

测试计算密集型

  1. from threading import Thread
  2. from multiprocessing import Process
  3. import time
  4. import os
  5. # 计算密集型
  6. def work1():
  7. number = 0
  8. for line in range(100000000):
  9. number += 1
  10. # IO密集型
  11. def work2():
  12. time.sleep(2)
  13. if __name__ == '__main__':
  14. # 测试计算密集型
  15. print(os.cpu_count()) # 4核cpu
  16. start = time.time()
  17. list1 = []
  18. for line in range(6):
  19. p = Process(target=work1) # 程序执行时间8.756593704223633
  20. # p = Thread(target=work1) # 程序执行时间31.78555393218994
  21. list1.append(p)
  22. p.start()
  23. for p in list1:
  24. p.join()
  25. end = time.time()
  26. print(f'程序执行时间{end - start}')

IO密集型

  1. from threading import Thread
  2. from multiprocessing import Process
  3. import time
  4. import os
  5. # 计算密集型
  6. def work1():
  7. number = 0
  8. for line in range(100000000):
  9. number += 1
  10. # IO密集型
  11. def work2():
  12. time.sleep(1)
  13. if __name__ == '__main__':
  14. # 测试计算密集型
  15. print(os.cpu_count()) # 4核cpu
  16. start = time.time()
  17. list1 = []
  18. for line in range(100):
  19. # p = Process(target=work2) # 程序执行时间15.354223251342773
  20. p = Thread(target=work2) # 程序执行时间1.0206732749938965
  21. list1.append(p)
  22. p.start()
  23. for p in list1:
  24. p.join()
  25. end = time.time()
  26. print(f'程序执行时间{end - start}')

结论:

  • 在计算密集型的情况下, 使用多进程
  • 在IO密集型的情况下, 使用多线程
  • 高效执行多个进程, 内有多个IO密集型程序,使用多进程 + 多线程

死锁现象

指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,如无外力作用,它们都无法推进下去.此时称系统处于死锁状态

以下就是死锁:

  1. from threading import Thread, Lock
  2. from threading import current_thread
  3. import time
  4. mutex_a = Lock()
  5. mutex_b = Lock()
  6. class MyThread(Thread):
  7. def run(self):
  8. self.func1()
  9. self.func2()
  10. def func1(self):
  11. mutex_a.acquire()
  12. print(f'用户{self.name}抢到锁a')
  13. mutex_b.acquire()
  14. print(f'用户{self.name}抢到锁b')
  15. mutex_b.release()
  16. print(f'用户{self.name}释放锁b')
  17. mutex_a.release()
  18. print(f'用户{self.name}释放锁a')
  19. def func2(self):
  20. mutex_b.acquire()
  21. print(f'用户{self.name}抢到锁b')
  22. time.sleep(1)
  23. mutex_a.acquire()
  24. print(f'用户{self.name}抢到锁a')
  25. mutex_a.release()
  26. print(f'用户{self.name}释放锁a')
  27. mutex_b.release()
  28. print(f'用户{self.name}释放锁b')
  29. for line in range(10):
  30. t = MyThread()
  31. t.start()
  32. '''
  33. 用户Thread-1抢到锁a
  34. 用户Thread-1抢到锁b
  35. 用户Thread-1释放锁b
  36. 用户Thread-1释放锁a
  37. 用户Thread-1抢到锁b
  38. 用户Thread-2抢到锁a
  39. '''
  40. # 一直等待

递归锁

用于解决死锁问题

RLock: 比喻成万能钥匙,可以提供给多个人使用

但是第一个使用的时候,会对该锁做一个引用计数

只有引用计数为0, 才能真正释放让一个人使用

上面的例子中用RLock代替Lock, 就不会发生死锁现象

  1. from threading import Thread, Lock, RLock
  2. from threading import current_thread
  3. import time
  4. # mutex_a = Lock()
  5. # mutex_b = Lock()
  6. mutex_a = mutex_b = RLock()
  7. class MyThread(Thread):
  8. def run(self):
  9. self.func1()
  10. self.func2()
  11. def func1(self):
  12. mutex_a.acquire()
  13. print(f'用户{self.name}抢到锁a')
  14. mutex_b.acquire()
  15. print(f'用户{self.name}抢到锁b')
  16. mutex_b.release()
  17. print(f'用户{self.name}释放锁b')
  18. mutex_a.release()
  19. print(f'用户{self.name}释放锁a')
  20. def func2(self):
  21. mutex_b.acquire()
  22. print(f'用户{self.name}抢到锁b')
  23. time.sleep(1)
  24. mutex_a.acquire()
  25. print(f'用户{self.name}抢到锁a')
  26. mutex_a.release()
  27. print(f'用户{self.name}释放锁a')
  28. mutex_b.release()
  29. print(f'用户{self.name}释放锁b')
  30. for line in range(10):
  31. t = MyThread()
  32. t.start()

信号量(了解)

互斥锁: 比喻成一个家用马桶, 同一时间只能让一个人去使用

信号比喻成公测多个马桶: 同一时间可以让多个人去使用

  1. from threading import Semaphore
  2. from threading import Thread
  3. from threading import current_thread
  4. import time
  5. sm = Semaphore(5)
  6. def task():
  7. sm.acquire()
  8. print(f'{current_thread().name}执行任务')
  9. time.sleep(1)
  10. sm.release()
  11. for i in range(20):
  12. t = Thread(target=task)
  13. t.start()

线程队列

线程Q: 就是线程队列 FIFO

  • 普通队列: 先进先出 FIFO
  • 特殊队列: 后进先出 LIFO
  • 优先级队列: 若传入一个元组,会依次判断参数的ASCII的数值大小
  1. import queue
  2. # 普通的线程队列: 遵循先进先出
  3. q = queue.Queue()
  4. q.put(1)
  5. q.put(2)
  6. q.put(3)
  7. print(q.get()) # 1
  8. print(q.get()) # 2
  9. # LIFO队列 后进先出
  10. q = queue.LifoQueue()
  11. q.put(1)
  12. q.put(2)
  13. q.put(3)
  14. print(q.get()) # 3
  15. # 优先级队列:根据参数内
  16. q = queue.PriorityQueue()
  17. q.put((4, '我'))
  18. q.put((2, '你'))
  19. q.put((3, 'ta'))
  20. print(q.get()) # (2, '你')

GIL全局解释器锁、死锁、递归锁、线程队列的更多相关文章

  1. python 线程(创建2种方式,锁,死锁,递归锁,GIL锁,守护进程)

    ###############总结############ 线程创建的2种方式(重点) 进程:资源分配单位    线程:cpu执行单位(实体) 线程的创建和销毁的开销特别小 线程之间资源共享,是同一个 ...

  2. day33 线程的创建 验证线程之间共享数据 守护线程 线程进程效率对比 锁 死锁 递归锁

    今日内容: 1.线程理论 2.锁: 牺牲了效率,保证了数据的安全(重点) 3.守护线程 4.GIL锁:(重点) 5.计算密集型和IO密集型 6.信号量,事件(了解) 7.补充. 子进程中不能input ...

  3. 并发编程8 线程的创建&验证线程之间数据共享&守护线程&线程进程效率对比&锁(死锁/递归锁)

    1.线程理论以及线程的两种创建方法 2.线程之间是数据共享的与join方法 3.多线程和多进程的效率对比 4.数据共享的补充线程开启太快 5.线程锁 互斥锁 同步锁 6.死锁现象和递归锁 7.守护线程 ...

  4. python并发编程-多线程实现服务端并发-GIL全局解释器锁-验证python多线程是否有用-死锁-递归锁-信号量-Event事件-线程结合队列-03

    目录 结合多线程实现服务端并发(不用socketserver模块) 服务端代码 客户端代码 CIL全局解释器锁****** 可能被问到的两个判断 与普通互斥锁的区别 验证python的多线程是否有用需 ...

  5. 10 并发编程-(线程)-GIL全局解释器锁&死锁与递归锁

    一.GIL全局解释器锁 1.引子 在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势 首先需要明确的一点是GIL并不是Python的特性,它是在实现Pyt ...

  6. 并发编程~~~多线程~~~守护线程, 互斥锁, 死锁现象与递归锁, 信号量 (Semaphore), GIL全局解释器锁

    一 守护线程 from threading import Thread import time def foo(): print(123) time.sleep(1) print('end123') ...

  7. python 之 并发编程(守护线程与守护进程的区别、线程互斥锁、死锁现象与递归锁、信号量、GIL全局解释器锁)

    9.94 守护线程与守护进程的区别 1.对主进程来说,运行完毕指的是主进程代码运行完毕2.对主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕​详细解释:1.主 ...

  8. 并发编程(五)——GIL全局解释器锁、死锁现象与递归锁、信号量、Event事件、线程queue

    GIL.死锁现象与递归锁.信号量.Event事件.线程queue 一.GIL全局解释器锁 1.什么是全局解释器锁 GIL本质就是一把互斥锁,相当于执行权限,每个进程内都会存在一把GIL,同一进程内的多 ...

  9. python基础--GIL全局解释器锁、Event事件、信号量、死锁、递归锁

    ps:python解释器有很多种,最常见的就是C python解释器 GIL全局解释器锁: GIL本质上是一把互斥锁:将并发变成串行,牺牲效率保证了数据的安全 用来阻止同一个进程下的多个线程的同时执行 ...

  10. TCP协议下的服务端并发,GIL全局解释器锁,死锁,信号量,event事件,线程q

    TCP协议下的服务端并发,GIL全局解释器锁,死锁,信号量,event事件,线程q 一.TCP协议下的服务端并发 ''' 将不同的功能尽量拆分成不同的函数,拆分出来的功能可以被多个地方使用 TCP服务 ...

随机推荐

  1. [转]Paste from Excel into C# app, retaining full precision

    本文转自:https://stackoverflow.com/questions/8614910/paste-from-excel-into-c-sharp-app-retaining-full-pr ...

  2. acwing 471. 棋盘 解题记录

    题解地址  https://www.acwing.com/problem/content/description/473/ 有一个m×m的棋盘,棋盘上每一个格子可能是红色.黄色或没有任何颜色的. 你现 ...

  3. NOIP2019游记

    道歉:本文章很快打完了,所以有脏话 第零天 早上睡到6点半九点半从学校出发 有种很忙碌的感觉 在车上异常无聊,和zsf用笔记本看了阿丽塔战斗天使,感觉蛮好看的 阿丽塔的形象很丰富 叙事由雾到开 推荐 ...

  4. QT新建空白项目-添加QT设计师界面类时出现的各种库无法导入识别

    按照教材上先新建一个空的项目--添加Qt设计师界面类时 出现各种 库无法识别 解决方法: 在 .pro文件中加入一行 QT += widgets 去构建中先执行 qmake 然后再构建一下  ok了 ...

  5. 使用canal增量同步mysql数据库信息到ElasticSearch

    本文介绍如何使用canal增量同步mysql数据库信息到ElasticSearch.(注意:是增量!!!) 1.简介 1.1 canal介绍 Canal是一个基于MySQL二进制日志的高性能数据同步系 ...

  6. 15-scrapy-redis两种形式分布式爬虫

    什么叫做分布式爬虫? 分布式爬虫,就是多台机器共用一个scrapy—redis程序高效爬取数据, 为啥要用分布式爬虫? 其一:因为多台机器上部署的scrapy会各自拥有各自的调度器,这样就使得多台机器 ...

  7. JMS入门Demo

    2.1点对点模式(邮箱) 点对点的模式主要建立在一个队列上面,当连接一个列队的时候,发送端不需要知道接收端是否正在接收,可以直接向ActiveMQ发送消息,发送的消息,将会先进入队列中,如果有接收端在 ...

  8. Spring5源码解析1-从启动容器开始

    从启动容器开始 最简单的启动spring的代码如下: @Configuration @ComponentScan public class AppConfig { } public class Mai ...

  9. MySQL 联表查询

    关系型数据库,免不了表之间存在各种引用与关联.这些关联是通过主键与外键搭配来形成的.所以,取数据时,很大情况下单张表无法满足需求,额外的数据则需要将其他表加入到查询中来,这便是 JOIN 关键字完成的 ...

  10. C#数组3(可变数组)

    using System; namespace class1 { class program { static void Main(string[] args) { ][];//这里的行必须定义好,但 ...