在python的爬虫,程序运行等等的应用场景中,按顺序的执行爬虫或者程序运行会花费大量的时间,通过并发来提高效率。可以通过四种方法来提升速度。分别为①单线程串行、②多线程并发、③多CPU并行、④多机器并行。CPU表示CPU的运算过程、IO指文件的读取过程,对程序来说是单线程串行,而对于单核CPU的机器来说是多线程并发CPU和CPU和IO可以同时进行,多核的CPU又可以实现多个CPU里的内核并行操作,对于机器也有并发。

①单线程串行 ②多线程并发 ③多CPU并行 ④多机器并行

python多线程、线程、协程

进程(thread): 进程是具有独立功能程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。

线程(thread): 线程是进程的一个实体,是cpu和分派的基本单位,它比进程更小的的独立运行的基本单位,线程自己基本不拥有系统资源,但它可与用属于一个进程的其他线程共享进程所拥有的全部资源。

协程(coroutine): 协程其实可以认为是比线程更小的执行单元,协程拥有自己的寄存器上下文和栈。我们把一个线程中的一个个函数叫做子程序,那么子程序在执行过程中可以中断去执行别的子程序,别的子程序也可以中断回来继续执行之前的子程序,这就是协程

多进程 多线程
数据共享、同步 数据共享复杂,需要用IPC;数据是分开的,同步简单 因为共享进程数据,数据共享简单,但也是因为这个原因导致同步复杂 各有优势
内存、CPU 占用内存多,切换复杂,CPU利用率低 占用内存少,切换简单,CPU利用率高 线程占优
创建销毁、切换 创建销毁、切换复杂,速度慢 创建销毁、切换简单,速度很快 线程占优
编程、调试 编程简单,调试简单 编程复杂,调试复杂 进程占优
可靠性 进程间不会互相影响 一个线程挂掉将导致整个进程挂掉 进程占优
分布式 适应于多核、多机分布式;如果一台机器不够,扩展到多台机器比较简单 适应于多核分布式 进程占优

进程示例

主程序中的进程关系

1
2
3
4
5
6
7
8
9
10
11
12
13
from multiprocessing import Process
import os

def run():
print(os.getpid(), os.getppid())

if __name__ == '__main__':
print('子进程:', os.getpid(),'父进程:', os.getppid()) #getpid获取子进程, getppid父进程
s = Process(target=run)
s.start()
#父进程不改变,子进程改变
>>>子进程: 17932 父进程: 7628
>>>13004 17932

multiprocessing模块

Process

1
2
3
4
5
6
7
8
9
10
Process中的构造方法为。
__init__(self, group=None, target=None, name=None, args=(), kwargs={}, daemon=None)

group:进程所属组

target:可调用对象(函数对象),为子进程对应的活动;相当于multiprocessing.Process子类化中重写的run()方法。
name:子进程的名称,默认(None)为"Process-N"。
args:进程活动(target)的非关键字参数,表示调用对象的位置参数元组
kwargs:进程活动(target)的关键字参数,表示调用对象的字典。
deamon:bool值,表示是否为守护进程。

Pool

1
2
3
4
5
6
7
8
Poll: multiprocessing.pool.Pool([processes[, initializer[, initargs[, maxtasksperchild[, context]]]]])
processes:选择工作进程数量,如果没有提供,将使用os.cpu_count()个进程(使用全部cpu)

initializer:如果该值不为None,就使用设置好的进程数工作

maxtasksperchild:选择进程退出替换新的任务之前可以完成的任务数,可以用来释放闲置资源,不设置时默认为None只要Pool未结束进程就一直存在

context:用在制定工作进程启动时的上下文,一般使用 multiprocessing.Pool() 或者一个context对象的Pool()方法来创建一个池,两种方法都合适的设置了context。

在Windows上要想使用进程模块,就必须把有关进程的代码写在if name == ‘__main__’ 内,否则在Windows下使用进程模块会产生异常。Unix/Linux下则不需要。