Python并发编程--多线程

admin 2023年5月15日01:28:46评论33 views字数 2253阅读7分30秒阅读模式

学习背景(优缺点)

优点:

  • 提高效率、执行同样的任务、减少时间消耗

  • 高并发、高扩展性、低成本

  • 需线程上下文切换的开销

缺点:

  • 编程复杂度会有所提高

  • 无法利用多核资源、协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上. 进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序



什么是CPU密集型计算、IO密集型计算


类型 概念 特点 举例
CPU密集型(CPU-bound) CPU密集型也叫计算密集型,是指IP在很短的时间就可以完成,CPU需要大量的计算和处理,特点是CPU占用率很高 本地(CPU)计算 正则匹配、压缩解压缩、加密解密等
IO密集型(IO-bound) IO密集型是指系统运作大部分的状况是在CPU在等待IO(内存/硬盘)读写造作时,占用率仍然较低 占用大量外部资源,如内存磁盘和网络 文件处理程序,网络爬虫程序,读写数据库程序


多线程、多进程、多协程的对比

三者关联:一个进程可启动N个线程,一个线程可以启动N个协程

三者优缺点:

类型 优点 缺点 适用场景
多进程 (python 的 multiprocessing模块) 可利用多核CPU并行计算 占用资源比线程多,也是最多的,可启动数量比线程少 CPU密集型计算
多线程 (python 的 threadin 模块) 相比进程,更轻量级,占用资源少 1. 相比进程,python多线程只能并发执行,不能同时利用多CPU(GIL即全局解释器锁),只能同时使用一个CPU 2. 相比协程,启动数目有限制,占用内存资源,有线程切换开销 IO密集型计算、同时运行的任务数目要求不多
多协程 (python 的 asynclo模块) 内存开销最少,启用数量可以很多,一个线程中可以开几万个协程,但是只是占用的本身线程的资源(一个线程) python支持的库有限制,例如python中使用多协程进行调用requests库是不支持的,这里要用 aiohttp 库,并且代码实现复杂 IO密集型计算,需要超多任务运行,但有线程库支持的场景(基础之上)


全局解释器锁(GIL)


python 速度慢的原因

原因一 python为动态语言,边解释边执行(源码翻译为机器码
原因二 GIL无法利用多核CPU并发执行


GIL是什么

官方的解释:GIL是用来防止多线程并发执行机器码的一个Mutex(互斥锁),原因是:Cpython的内存管理是 not thread-safe

全局解释器锁:是计算机程序设计语言解释器用于同步线程的一种机制,他使得任何时刻仅有一个线程在执行。即便在多核心处理器上,使用GIL的解释器也只允许同一时间执行一个线程

Python并发编程--多线程

如上图解释,多线程为流水线操作,其实可以说在有GIL的情况下,启用多个线程,同一时间还是进行的一个任务,并没有实质的并发操作,即使机器为多核CPU运转,在GIL的存在下,单个时刻也只能使用一个,相比并发加速的C++/Java(同一时刻可以并发执行) 慢很多

为什么要有GIL

Python设计初期,为了规避并发问题引入了GIL,但是现在想要去除,官方大大发现去除不掉了

为了解决多线程之间数据完整性和状态同步问题

原因详解:

Python中对象的管理,是使用与i你用计数器进行的,引用数为0则释放对象

假设,开了有两个线程,来处理对象obj,如图:

Python并发编程--多线程

然而,GIL的出现解决了此类问题,简化了python对共享资源的管理

规避GIL带来的限制

多线程threading 机制依然是有用的,用于IO密集型计算

一、在IO操作期间(文件读写等),线程对释放GIL,实现CPU和IO的并行,但是多线程用于CPU密集型计算时,只会更加拖慢速度

二、使用multiprocessing 的多线程机制实现真正的并行计算,利用多核CPU优势

多线程使用举例

import threadingimport requestsimport time
def url_blog(): url_lst = [] for page in range(3): url = f'https://www.cnblogs.com/#p{page}' url_lst.append(url) return url_lstdef blog(url): response = requests.get(url).text print(len(response))def info_blog(): url_lst = url_blog() treads = [] #创建泪飙存储url,方便后面为每个url创建多线程 for url in url_lst: #为每个url操作创建一个线程 treads.append( threading.Thread(target=blog, args=(url,)) ) for tread in treads: tread.start() #启动每个线程 for tread in treads: tread.join() #结束每个线程 print("线程结束")if __name__ == '__main__': start_time = time.time() info_blog() end_time = time.time() print(end_time-start_time)

        

                                                        



                                                                    愿爱无忧



Python并发编程--多线程

end

        

原文始发于微信公众号(朱厌安全团队):Python并发编程--多线程

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年5月15日01:28:46
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Python并发编程--多线程https://cn-sec.com/archives/1729619.html

发表评论

匿名网友 填写信息