IO密集型和CPU密集型
我们会针对不同的任务类型来决定是否使用python的多线程。
IO密集型
一般是指磁盘读写,例如读取和写入文件等等,和网络应用,典型的有网络爬虫等。因为这些操作限制你的并不是程序的性能,而是硬盘读写的速度或者网络的速度。
CPU密集型
主要就是指计算任务。此时需要发挥的就是程序本身的性能了,和其他的速度关系不大。
线程和进程
线程
所有的线程都在一个进程中,他们会自己记录自己在一个任务中运行到了说明地方,以便重新调用的时候继续运行。各个线程之间共享同一份数据,也能更方便地通讯和同步。但是要注意避免由于访问数据顺序不同而导致数据结果不同步的现象。
同步机制
python包含了多种线程间数据同步的机制。其中最常用就是队列Queue
队列模式
其核心思想就是排队。利用Queue
模块将需要处理的任务步骤加入队列中,基本按照先来后到的方式进行处理,当然也可以设置优先级。
线程池
因为线程的创建和销毁都是需要耗费资源的,在执行任务的过程中不断地创建和销毁线程是很浪费的一件事情。那么我们可以利用池的概念,先创建一些线程把他们放到线程池中,需要用的时候就拿出来用,不需要用的时候就放回去而不是销毁掉。通过这样的方式来节省资源。
进程
一个进程就是一次程序的执行,每个进程都有自己独立的内存,地址和其他数据。进程可以由操作系统控制运行。在进行CPU密集型任务的时候,由于python有GIL的限制,不能使用多线程,却可以使用多进程来并行计算。
GIL
全局解释锁(Global Interpreter Lock),是Cpython特有的为了保护线程之间数据的完整性和同步性而特别设置的。有了这个锁以后,多线程的CPU通通变成了单线程的了。
但是,如果我们的目标是要进行IO密集型的任务的时候,由于其他速度和性能的限制,比如读写进度,网络响应等等,那么此时python的多线程就会在等待的时间被充分利用了。如果是CPU密集型的任务,由于GIL的关系,首先多线程的效率理论上和单线程一样,但是线程之间的通信也会有消耗,其结果就是多线程反而不如单线程了。
Python的并发
1.使用threading
库在单独的线程中执行任意python可调用对象
from threading import Thread
t = Thread(target=func,args=(params,))
# 开始线程
t.start()
# 连接线程
t.join()
2.使用Queue
模块来实现线程之间的通信
from queue import Queue
# 加入队列
Queuq.put()
3.线程池
from concurrent.futures import ThreadPoolExecutor
4.进程池
from concurrent.futures import ProcessPoolExecutor
with ProcessPoolExecutor() as pool:
do work in parallel using pool