目录
1、什么是多任务处理
一台计算机可以同时处理多个任务,叫做多任务处理。实现多任务有三种方式:多进程、多线程、协程。
单核CPU在处理多个任务时,比如有QQ,微信,网易云音乐,QQ只占用CPU的0.01秒,然后QQ会切出去,微信再占用0.01秒,然后微信会被切出去,网易云音乐再占用0.01s,CPU频繁快速地切换,使人无法察觉,感觉这三个任务时同时进行的。所以当开很多任务时,电脑会变卡。 (ps:有几个CPU就叫做几核CPU)
补充两个概念:
①高并发:
任务管理器的进程可以理解为正在运行当中的程序。
进程数远远大于CPU的核数,此时叫做高并发。比如图中有100多个进程,但电脑是双核CPU,此时就是高并发。
②高并行:CPU的核数多,同时运行的程序多。比如有50核,可以同时有50个程序运行
2、简述多进程
多个任务同时在跑叫做多进程。
程序:一个指令的集合。比如我们写的python代码,每一行看做一个指令,这么多指令放在一起,没有执行,就是程序
进程:正在执行的程序
一般的python代码都是从上往下执行(单进程),不是多进程
程序开始运行时,首先创建一个主进程(父进程),在主进程下,可以创建新的进程(子进程),子进程不能创建新的子进程,子进程依赖于主进程,主进程结束,子进程也会结束。python提供了多进程包multiprocessing,借助这个包,可以完成从单进程到并发执行的转换
3、创建一个进程
首先应该调用包
import multiprocessing
但因为包里很多东西不会用到,所以一般采用
from multiprocessing import 想引用的玩意
故,如下:
from multiprocessing import Process
Process是进程类,通过它,可以创建一个进程对象,就可以让进程对象去执行某个函数,Process中的参数target应赋值函数名,告诉计算机当前创建的进程对象是运行哪个函数;参数args放置被执行函数的参数,args应赋值位置参数元组(给函数参数首先是一个元组,另外在位置上、数量上、类型上要一一对应),注意若元组只有一个元素,则需要在后面加逗号。
包中的start()会执行相应的函数;因为主进程结束,子进程也会结束,所以用join()保证子进程的完整运行,防止主进程结束,而子进程未运行完。
在win系统,python3情况下,还应添加如下,才能保证正常运行
if __name__ == "__main__":
所以代码如下:
from multiprocessing import Process
def run1():
print('wulala')
def run2():
print('hahaha')
def run3(name):
print('My name is %s'%(name))
if __name__ == "__main__":
p1 = Process(target=run1,)
p1.start()#调用start,会执行对应的run1函数
p1.join()#保证子进程的完成
p2 = Process(target=run2)
p2.start()
p2.join()
p3 = Process(target=run3,args=('Python',))
p3.start()
p3.join()
运行的结果如下:
画一个执行顺序:
Process中还有一个属性是name,可有可无,是子进程的名称,如果不被设置,则系统自己设置
如,修改/添加以下代码:
p1 = Process(name="进程1",target=run1,)
print(p1.name)
结果为:
至于 if __name__ == "__main__":
.py文件有两种作用,一种是直接作为程序来运行,二种可以导入到其他python程序当中。p1,p2,p3子进程是由主进程创建的,创建的时候,子进程会默认导入主进程的所有代码,就会变成递归创建(子进程创建子进程是不允许的),而且没有结束条件,会报错。__name__会表示当前模块的名字,如果在主进程里,则__name__就是__main__,子进程里的模块名不是__main__,则不被执行。
4、Process中常用的方法和属性
---p.start() 启动进程,调用该子进程中的p.run()
---p.run() 进程启动时运行的方法,正式它去调用target指定的函数。
---p.terminate() 强制终止进程p,不会进行任何清理操作。(仍占用内存)
---p.is_alive() 如果p仍然运行,返回True,用来判断进程是否仍运行
---p.join([timeout]) 主进程等待p终止,timeout是可选的超时时间。阻塞住主进程,在等待子进程结束,然后再往下执行
---name 当前进程实例的别名,默认为Process-N,N是从1开始递增的整数
---pid 当前进程实例的PID值。系统用来区分不同的进程
5、多个进程的运行问题
from multiprocessing import Process
num = 1
def run1():
global num
num += 5
print("子进程1中num=%d"%(num))
def run2():
global num
num += 10
print("子进程2中num=%d"%(num))
if __name__ == "__main__":
print("父进程启动")
p1 = Process(target=run1)
p2 = Process(target=run2)
p1.start()
p2.start()
p1.join()
p2.join()
print(num)
运行结果为:
多进程之间,调用的时候是无序的。如果一定想p1执行完在执行p2,则在p1.start()后直接加join()
p1.start()之后并不是马上执行p1,而是等待系统调度,等待时,主进程会继续往下走,若又一个子进程等待调度,这两个子进程谁先开始不确定。因为此时代码较少,所以大部分情况是p1先执行。
至于结果是11,6,1,原因是多个进程之间的数据默认不共享,创建子进程时,导入父进程所有代码,num都是1,所以6,11,主进程依然是1。
6、使用Process子类创建对象
import multiprocessing
import time
class MyProcess(multiprocessing.Process):
def run(self):#方法重写
n = 5
while n > 0:
print(n)
time.sleep(1)
n -= 1
if __name__ == "__main__":
p = MyProcess()#不用指明target
p.start()
p.join()
因为是run()函数使得子进程运行,所以重新写run()函数。因为将代码写入了run,已经指明了run 函数要执行的内容,所以不用写target。
7、进程池
Process、子类、进程池是创建子进程的三种方法,子进程数量多时候,适用进程池。
from multiprocessing import Pool
import time
def work(num):
print(num)
time.sleep(1)
if __name__ == "__main__":
po = Pool(2)#两个进程同时运行
for i in range(20):#有20个进程被进程池创建,同时最多只能两个运行
po.apply_async(work,(i,))#是一个元组
po.close()#关闭进程池,表示不再接受新请求,原进程不会删除
po.join()#一定要在close后面
po = Pool(10)表示同时有10个进程池创建出来的进程在运行,如果不赋值,则默认为电脑CPU数
po.apply_async(work,(i,))如果执行的函数有参数,则逗号分隔,添加元组
进程池创建的函数,不用再调用start,会自己排队去执行
multiprocessing.Pool常用的函数:
apply_async:使用非阻塞方式调用函数
apply:使用阻塞方式调用函数,同一时间只允许一个子进程运行
close:关闭Pool,使其不再接受新的任务
terminate:不管任务是否完成,立即终止
join:用法和之前相同
进程池创建的函数,不用再调用start,会自己排队去执行