python 多线程和多进程
在真实业务中不单单会涉及CPU计算,还有网络IO和磁盘IO处理,这些处理是非常耗时的。如果一个线程整个流程是上图的流程,真正涉及到CPU的只有2个节点,其他的节点都是IO处理,那么线程在做IO处理的时候,CPU就空闲出来了,CPU的利用率就不高。
多线程:提升CPU利用率。
最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目
如:我们的服务器CPU核数为4核,一个任务线程cpu耗时为20ms,线程等待(网络IO、磁盘IO)耗时80ms,那最佳线程数目:( 80 + 20 )/20 * 4 = 20。也就是设置20个线程数最佳。
CPU密集型:操作内存处理的业务,一般线程数设置为:CPU核数 + 1 或者 CPU核数*2。核数为4的话,一般设置 5 或 8
IO密集型:文件操作,网络操作,数据库操作,一般线程设置为:cpu核数 / (1-0.9),核数为4的话,一般设置 40
python中具备进程池和线程池来分别管理多进程和多线程
multiprocessing.Pool
multithreading.Pool
ThreadPoolExecutor 线程池
ProcessPoolExecutor进程池
GIL (Global Interpreter Lock) 是 Python 部分解释器的一个重要特性。GIL 是一个全局锁,它限制了【一个进程】一次只能有【一个线程】在运行 Python 解释器中的字节码。即使你的程序有多个线程,在任意时刻,只有一个线程可以执行 Python 代码。
GIL 的设计是为了解决 Python 的多线程环境中的同步问题。在 Python 中,所有的数据都是全局的,如果多个线程同时访问和修改共享数据,可能导致不一致性和数据损坏。
GIL 的设计是为了避免这种情况,使得所有的线程在任意时刻只有一个线程在执行 Python 代码。这保证了在多线程环境中,所有数据的一致性。
但是,GIL 的设计也带来了一些副作用,尤其是在 CPU 密集型任务中。GIL 的存在限制了 CPU 利用率,导致 Python 的多线程性能较差。因此,在 CPU 密集型任务中,通常使用多进程代替多线程;
在IO密集型任务中,虽然有GIL的存在,单多线程还是能够提升一定的执行效率。
因为在IO密集型任务中,使用CPU的时间并不多,主要瓶颈还是在IO等待上;
单线程和多线程进行比较时,会发现多线程的执行速度比单线程要快很多,因为大多数时间都在等待IO操作,单线程必须等前一个IO操作完成后才能处理后面的程序,而使用多线程时,若当前线程进入了IO操作,解释器会很快将切换其他线程进行运行,一定程度上将多个线程的IO操作变成了类【并行】运行,非IO程序的执行多线程还是在【并发】执行。
总结:
IO密集型:Python的线程,因为GIL(Global Interpreter Lock,全局解释器锁)的原因,是不能并发到多个物理核心上的。GIL对I / O绑定多线程程序的性能影响不大,因为线程在等待I / O时共享锁。但是线程完全受CPU限制的程序,例如,使用线程处理部分图像的程序,不仅会因锁定而成为单线程,而且还会看到执行时间的增加, 所以是IO密集型的,像爬虫,读写文件,使用线程池是ok的。
CPU密集型:在针对CPU密集型的时候使用多个进程而不是线程。每个Python进程都有自己的Python解释器和内存空间。