一、多进程
1.1、概念
进程是程序在计算机上的一次执行活动。当你运行一个程序,你就启动了一个进程。显然,程序是死的(静态的),进程是活的(动态的)。进程可以分为系统进程和用户进程。凡是用于完成操作系统的各种功能的进程就是系统进程,它们就是处于运行状态下的操作系统本身;用户进程就不必我多讲了吧,所有由你启动的进程都是用户进程。进程是操作系统进行资源分配的单位。它的思想简单介绍如下:
在操作系统的管理下,所有正在运行的进程轮流使用CPU,每个进程允许占用CPU的时间非常短(比如10毫秒),这样用户根本感觉不出来CPU是在轮流为多个进程服务,就好象所有的进程都在不间断地运行一样。但实际上在任何一个时间内有且仅有一个进程占有CPU。
shell中使用多进程的类型:
#!/bin/bashtestsleep(){ echo "start sleep" sleep 5 echo "end sleep"}echo "start main"testsleep & #&后台运行的模式就是使用了多进程的理念echo "end main"
1.2、多进程和多线程的区别:
- 多进程使用的是cpu的多个核,适合运算密集型
- 多线程使用的是cpu的一个核,适合io密集型
1.3、组件
Python提供了非常好用的多进程包,multiprocessing,我们在使用的时候,只需要导入该模块就可以了。Multiprocessing支持子进程,通信,共享数据,执行不同形式的同步,提供了Process,Pipe, Lock等组件
1.4、示例:
import multiprocessing import time def worker(args, interval): print("start worker {0}".format(args)) time.sleep(interval) print("end worker {0}".format(args)) def main(): print("start main") p1 = multiprocessing.Process(target=worker, args=(1, 1)) p2 = multiprocessing.Process(target=worker, args=(2, 2)) p3 = multiprocessing.Process(target=worker, args=(3, 3)) p1.start() p2.start() p3.start() print("end main") if __name__ == '__main__': main() # p = multiprocessing.Process(target=, args=) # target 指定的是当进程执行时,需要执行的函数 # args 是当进程执行时,需要给函数传入的参数 # 注意: args必须是一个tuple, 特别是当函数需要传入一个参数时 (1,) # p 代表的是一个多进程, # p.is_alive() 判断进程是否存活 # p.run() 启动进程 # p.start() 启动进程,他会自动调用run方法,推荐使用start # p.join(timeout) 等待子进程结束或者到超时时间 # p.terminate() 强制子进程退出 # p.name 进程的名字 # p.pid 进程的pid
1.5、示例:
import multiprocessing import time def worker(args, interval): print("start worker {0}".format(args)) time.sleep(interval) print("end worker {0}".format(args)) def main(): print("start main") p1 = multiprocessing.Process(target=worker, args=(1, 1)) p2 = multiprocessing.Process(target=worker, args=(2, 2)) p3 = multiprocessing.Process(target=worker, args=(3, 3)) p1.start() p1.join(timeout=0.5) p2.start() p3.start() print("the number of CPU is: {0}".format(multiprocessing.cpu_count())) for p in multiprocessing.active_children(): #打印存活的子进程 print("The name of active children is: {0}, pid is: {1} is alive".format(p.name, p.pid)) print("end main") if __name__ == '__main__': main()
1.6、Lock组件
当我们用多进程来读写文件的时候,如果一个进程是写文件,一个进程是读文件,如果两个文件同时进行,肯定是不行的,必须是文件写结束以后,才可以进行读操作。或者是多个进程在共享一些资源的时候,同时只能有一个进程进行访问,那就要有一个锁机制进行控制。
需求:一个进程写入一个文件,一个进程追加文件,一个进程读文件,同时启动起来我们可以通过进程的join()方法来实现,但是为了学习Lock,用Lock来实现。先看不加锁程序,在看加锁程序,最后比较两个程序的区别
示例:
import time import multiprocessing def add1(lock, value, number): with lock: #这里是第一种调用锁的方法,好处是能自动释放锁 print("start add1 number= {0}".format(number)) for i in range(1, 5): number += value time.sleep(0.3) print("number = {0}".format(number)) def add3(lock, value, number): lock.acquire() #这里是第二种调用锁的方法,在下面需要有释放锁的操作 print("start add3 number= {0}".format(number)) try: for i in range(1, 5): number += value time.sleep(0.3) print("number = {0}".format(number)) except Exception as e: raise e finally: lock.release() if __name__ == '__main__': print("start main") number = 0 lock = multiprocessing.Lock() #这里创建了锁的机制,目的是为了避免进程间无序执行 p1 = multiprocessing.Process(target=add1, args=(lock, 1, number)) p3 = multiprocessing.Process(target=add3, args=(lock, 3, number)) p1.start() p3.start() print("end main")
1.7、共享内存
python的multiprocessing模块也给我们提供了共享内存的操作
一般的变量在进程之间是没法进行通讯的,multiprocessing给我们提供了Value和Array模块,他们可以在不通的进程中共同使用
示例:
import multiprocessing from multiprocessing import Value, Array, Manager def add1(value, number): print("start add1 number= {0}".format(number.value)) for i in range(1, 5): number.value += value print("number = {0}".format(number.value)) def add3(value, number): print("start add3 number= {0}".format(number.value)) try: for i in range(1, 5): number.value += value print("number = {0}".format(number.value)) except Exception as e: raise e if __name__ == '__main__': print("start main") number = Value('d', 0) #这里创建了一个共享内存 p1 = multiprocessing.Process(target=add1, args=(1, number)) p3 = multiprocessing.Process(target=add3, args=(3, number)) p1.start() p3.start() print("end main") 输出结果: start main end main start add1 number= 0.0 number = 1.0 number = 2.0 number = 3.0 number = 4.0 start add3 number= 4.0 number = 7.0 number = 10.0 number = 13.0 number = 16.0 #这里运行的结果也是按照每个进程顺序来的,可以得出的是在共享内存里会默认给每个进程加一个锁,避免进程间乱套
以上实现的数据共享的方式只有两种结构Value和Array。Python中提供了强大的Manage专门用来做数据共享的,其支持的类型非常多,包括,Value, Array,list,dict, Queue, Lock等。
下面看个例子:
from multiprocessing import Process, Managerdef func(dt, lt):for i in range(10): key = 'arg' + str(i) dt[key] = i * i lt += range(11, 16)if __name__ == "__main__": manager = Manager() dt = manager.dict() lt = manager.list() p = Process(target=func, args=(dt, lt)) p.start() p.join()print(dt)print(lt)
1.8、进程池
Pool可以提供指定数量的进程,供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程。
示例:
import time import multiprocessing def fun(msg): print("#########start#### {0}".format(msg)) time.sleep(3) print("#########end###### {0}".format(msg)) if __name__ == '__main__': print("start main") pool = multiprocessing.Pool(processes=3) for i in range(1, 7): msg = "hello {0}".format(i) pool.apply_async(fun, (msg,))# 执行时间6s+ # pool.apply(fun, (msg,)) 6*3=18+#执行时间 pool.close()#在调用join之前,要先调用close,否则会报错,close执行完不会有新的进程加入到pool pool.join()#join 是等待所有的子进程结束 print("end main")
1.9、进程池阻塞和非阻塞的区别:
- Pool.apply_async 非阻塞,定义的进程池进程最大数可以同时执行。
- Pool.apply 一个进程结束,释放回进程池,下一个进程才可以开始