通过学习bi站 蚂蚁学Python
老师视频总结文档,仅用于学习。。。
多线程:threading,利用CPU和IO可以同时执行的原理,不会让CPU干巴巴的等待IO完成
多进程:multiprocessing,利用多核CPU的能力,真正的并行执行任务
异步IO:asyncio,在单线程利用CPU和IO同时执行的原理,实现函数异步执行
方法:
使用Lock对资源加锁,防止冲突访问(锁起来就可以实现有序访问)
使用Queue实现不同线程/进程之间的数据通信,实现生产者—消费者模式
使用线程池Pool/进程池Pool,简化线程/进程的任务提交、等待结束、获取结果
使用subprocess 启动外部程序的进程,并进行输入输出交互
CPU密集型也叫计算密集型,是指在I/O在很短的时间就可以完成,CPU需要大量的计算和处理,特点是CPU占用率相当高
例如:
- 压缩和解压缩
- 加密解密
- 正则表达式搜索
IO密集型是指是,系统运作大部分的状况是CPU在等I/O (硬盘/内存) 的读/写操作,CPU占用率仍然较低
例如:
- 文件处理程序(大量读写程序)
- 网络爬虫程序(大量网络下载,网络下载占用很长时间)
- 读写数据库程序(网络的读取,网络开销比较多)
优点:可以利用多核CPU并行运算
缺点:占用资源最多、可启动数目比线程少
适用于:CPU密集型计算
一个进程可以启动N个线程
优点:相比进程,更轻量级、占用资源少
缺点:
适用于:IO 密集型计算、同时运行的任务数目要求不多
一个线程可以启动N个协程
python慢的原因
- 动态类语言,边解释边执行
- GIL的存在导致python无法利用多核CPU并发执行
全局解释器锁(Global Interpreter Lock)
是计算机程序设计语言解释器用于 同步线程的一种机制,它使得任何时刻仅有一个线程在执行
即便在多核处理器上,使用GIL的解释器也只允许同一时间执行一个线程。
即使电脑有多核CPU单个时刻也只能使用一个,相比较于并发加速的C++/Java慢很多
Python设计初期,为了规避并发问题引入了GIL,现在想去除却去除不掉了
引入GIL的原因:
为了解决线程之间数据完整性和状态同步问题
Python中对象的管理,是使用引用计数器进行的,引用数为0则释放对象
例:线程A和线程B都引用的对象obj,obj.ref_num = 2,假如线程A和线程B都想撤销对obj的引用
1、多线程
threading
机制 还是有用的,用于IO密集型计算
因为在
I/O
(read、write、send、recv、etc.)期间,线程会释放GIL
, 实现CPU和IO并行,因此多线程用于IO密集型计算,依然可以大幅度提升速度
但是,多线程用于CPU密集型计算,只会拖慢速度
2、使用
multiprocessing
的多进程机制实现并行计算、利用多核CPU优势(为了应对GIL的问题,Python提供了multiprocessing)
指的是任务数多余cpu核数,通过操作系统的各种任务调度算法,实现用多个任务“一起”执 行(实际上总有一些任务不在执行,因为切换任务的速度相当快,看上去一起执行而已)
指的是任务数小于等于cpu核数,即任务真的是一起执行的。
1、准备一个函数
def my_func(a,b) do_sum(a,b)
2、怎样创建一个线程
import threading t = threading.Thread(target=my_func, args=(100,200)) # target 表示要执行的任务(传入函数名),args表示传入函数的参数
3、启动线程
t.start()
4、等待结束
t.join()
import blog_spider
import threading
import time
# 单线程
def single_thread():
print("single_thread begin")
for url in blog_spider.urls:
blog_spider.craw(url)
print("single_thread end")
# 多线程
def multi_thread():
print("multi_thread begin")
threads = []
for url in blog_spider.urls:
threads.append(
threading.Thread(target=blog_spider.craw, args=(url,))
)
for thread in threads:
thread.start() # 开启线程
for thread in threads:
thread.join() # 等待线程结束
print("multi_thread end")
if __name__ == '__main__':
start = time.time() # 开始时间
single_thread()
end = time.time() # 结束时间
print("single thread cost:", end-start, "seconds") # single thread cost: 8.59098196029663 seconds
start = time.time()
multi_thread()
end = time.time()
print("multi thread cost:", end - start, "seconds") # multi thread cost: 0.82523512840271 seconds
线程的生命周期:
新建线程系统需要分配资源、终止线程系统需要回收资源,如果可以重用线程,则可以减去新建/终止的开销