多进程
什么是多任务
什么叫做多任务呢?简单的说,就是操作系统(OS)可以同时运行多个任务。如你可以一边浏览网页,一边听着歌,同时还可以使用画板画着画,这个就是多任务。其实我们的操作系统就是多任务。
单核CPU:
1、时间片轮换
2、优先级别调度
多核CPU:
1、并发
2、并行
Linux下fork实现多进程
进程:
◆编写完的代码,在没有运行的情况下,称之为程序。
◆正在运行的代码,就成为了进程。
◆进程,除了包含代码外,还需要运行环境等,所以和程序是存在区别的。
◆进行多进程操作时,肯定会存在一个主进程。
fork()方法
Python在os模块上封装了常用的系统调用,其中就包括了fork,我们可以很轻松地在Python代码中创建进程。fork() 函数可以获得系统中进程的PID ( Process ID ),返回0则为子进程,否则就是父进程,然后可以据此对运行中的进程进行操作;但是os.fork()只能在Linux下运行,可以使用os.getpid()获取该进程的进程号,os.getppid()获取子进程的父进程进程号。
import os
pid = os.fork()
if pid == 0:
while True:
print("执行子进程!我的进程id:%s,我的父进程id:%s" % (os.getpid(),os.getppid()))
else:
while True:
print("执行主进程!我的进程id:%s,我的子进程id:%s" % (os.getpid(),pid))
#运行结果
>>>执行子进程!我的进程id:2978,我的父进程id:2977
>>>执行主进程!我的进程id:2977,我的子进程id:2978
>>>执行主进程!我的进程id:2977,我的子进程id:2978
>>>执行子进程!我的进程id:2978,我的父进程id:2977
>>>...
#假如多进程中有全局变量的使用
import os
pid = os.fork()
num = 0
if pid == 0:
num +=1
print("子进程" + num)
else:
num += 1
print("父进程" + num)
#运行结果
>>>子进程1
>>>父进程1
#当多个fork存在时的运行结果
import os
pid1 = os.fork()
if pid1 == 0:
print("我是进程1")
else:
print("我是进程2")
pid2 = os.fork()
if pid2 == 0:
print("我是进程3")
else:
pirnt("我是进程4")
#运行结果
>>>我是进程1
>>>我是进程3
>>>我是进程4
>>>我是进程2
>>>我是进程3
>>>我是进程4
观察运行结果发现:
◆两个进程的运行的顺序是无序的;
◆进程之间的数据无法共享,是各自拥有的一份;
◆当程序遇到fork时程序会被分成一个子进程一个主进程,然后再次遇到fork子进程又被分成一个主进程和一个子进程,主进程也被分成一个子进程和主进程,就产生了上面的第三个运行结果;
函数多进程
multiprocessing引入多进程
fork()方法只能使用在linux系统下(tornado框架的高效性就是使用一个单线程通过fork()实现同时服务多个客户端)
python中在linux和windows下都能使用的多线程是multiprocessing中的Process类,通过Process实现一个多进程
# 引入模块
from multiprocessing import Process
import time
# 定义一个函数,将来定义成一个进程
def pro1(msg):
for i in range(5):
print("进程%s正在运行" % msg )
time.sleep(1)
else:
print("进程%s已结束" % msg)
def pro2(msg):
for i in range(10):
print("进程%s正在运行" % msg )
time.sleep(1)
else:
print("进程%s已结束" % msg)
# 主进程开始,然后启动子进程
if __name__ == "__main__":
"""
1、多进程必须需要主进程才能执行,python中__main__就是主进程的位置
2、将定义好的函数转换成进程,只需要将Process类实例化,必备属性有:
①target:Process的一个属性,值是要转换成子进程的函数名
②args:它必须是一个元组,保存所有要用到的参数,若只有一个参数,写成(val,)的形式
"""
print("————————主进程启动————————")
bilibili = Process(target=pro1,args=("哔哩哔哩",),name="哔哩哔哩干杯")
dnf = Process(target=pro2,args=("掉线城与勇士",),name="CNM给我出时光鞋")
# 开启子进程
print(bilibili.name)
bilibili.start()
# 这里可以使用一下进程对象的terminate方法,终止一个进程
if bilibili.is_alive():
bilibili.terminate()
print("哔哩哔哩已结束")
print(dnf.name)
dnf.start()
# 设置主进程等待子进程结束在结束自己本身,当有多个进程时
# 等待哪个进程就会在哪个进程结束后向下执行,
bilibili.join()
print("————————主进程结束————————")
# 运行结果
D:\python软件\python.exe D:/python_test/re_test.py
————————主进程启动————————
哔哩哔哩干杯
哔哩哔哩已结束
CNM给我出时光鞋
————————主进程结束————————
进程掉线城与勇士正在运行
进程掉线城与勇士正在运行
进程掉线城与勇士正在运行
进程掉线城与勇士正在运行
进程掉线城与勇士正在运行
进程掉线城与勇士正在运行
进程掉线城与勇士正在运行
进程掉线城与勇士正在运行
进程掉线城与勇士正在运行
进程掉线城与勇士正在运行
进程掉线城与勇士已结束
Process finished with exit code 0
总结:
1、主进程是必须的
2、创建进程实例时target目标是函数名不是调用函数
3、args属性必须是一个元组,只有一个参数需要时写成(val,)
4、termanite()终止方法,结束进程
5、多个子进程存在时,设置主进程等待哪个子进程,就在哪个子进程结束后继续向下执行主进程
类的多进程
创建一个子进程类的步骤
1、定义一个类,继承自Process父类
2、如果需要父类中的某些属性使用supper().__init__()或Process.__init__()调用
3、重写Process父类的run方法,在run方法中写入需要执行的进程
from multiprocessing import Process
class MyProcess(Process):
def __init__(self,name,msg):
super().__init__(name=name)
self.msg = msg
#重写run方法
def run(self):
print(self.name)
print("子进程%s开始运行" % self.msg)
# 主进程入口
if __name__ == "__main__":
print("————————主进程开始————————")
# 定义出子进程的实例,并执行子进程
for i in range(5):
m = MyProcess("bilibli","哔哩哔哩动画")
m.start()
m.join()
print("————————主进程结束————————")
#运行结果
D:\python软件\python.exe D:/python_test/re_test.py
————————主进程开始————————
bilibli
子进程哔哩哔哩动画开始运行
bilibli
子进程哔哩哔哩动画开始运行
bilibli
子进程哔哩哔哩动画开始运行
bilibli
子进程哔哩哔哩动画开始运行
bilibli
子进程哔哩哔哩动画开始运行
————————主进程结束————————
Process finished with exit code 0
进程池申请异步
进程池概念
当我们有很多大量的进程需要执行时(例如多个用户访问服务器,每个用户都是一个进程);
此时若继续使用Process创建多进程,需要多次创建,大大加重了内存的使用和代码量;
使用进程池Pool保存进程,能提前创建规定数量的进程,通过遍历一次得到多个进程;
达到并发并行的效果
定义一个简单的进程池
"""
按照顺序用进程池申请异步的方法——apply_async()
"""
#引入需要的模块
from multiprocessing import Pool
# 定义进程对象的目标函数
def run(msg):
print("进程%s运行了" %msg)
#主进程入口
if __name__ == "__main__":
print("主进程开始")
# 定义一个进程池,池中保存3个进程
p = Pool(3)
# 遍历进程池获取一次获取3个进程
for i in range(10):
#进程池实例的apply_async()方法申请异步,实现并发并行,参数有target,args,name等
p.apply_async(run,("开启session",))
#执行完毕后一定要关闭进程池
p.close()
#这里是重点,进程池中子进程类似于守护进程,
#当主进程结束后,子进程自动结束,所以必须设置主进程等待
p.join()
print("主进程结束")
#运行结果
D:\python软件\python.exe D:/_web_project/tetetetetet.py
主进程开始
进程开启session运行了
进程开启session运行了
进程开启session运行了
进程开启session运行了
进程开启session运行了
进程开启session运行了
进程开启session运行了
进程开启session运行了
进程开启session运行了
进程开启session运行了
主进程结束
Process finished with exit code 0
总结重点
1、定义进程池实例时要设定池中一次性存放几个子进程;
2、执行结果是一次出现3个然后出现3次,最后一次只出现了一个;
3、子进程执行完毕后,必须关闭进程池;
4、主进程必须等待进程池中的子进程执行完毕后再结束。【守护进程:跟随主进程的结束而结束】
进程中的数据传递和数据共享
在上面的案例中可以看出,进程间的数据都是单独的一份,不会发生影响;但是进程之间的数据并不是绝对隔离的,可以实现使用Queue进行进程的数据传递,使用Manager实现进程间的数据共享。
Queue数据传递
Queue是一种特殊的先进先出的线性数据表,一般的数据队列都有大小限制,等待超时时间需要自己定义,常用方法有:
◆put()放入数据到队列中
◆get()从队列中取出数据
◆empty()清空队列
from multiprocessing import Process,Queue
#子进程
def put_pro(q,n):
#向队列添加数据
q.put("第%s个进程添加了一个数据" % n)
#主进程
def get_pro(q):
print("————————————start————————————")
for i in range(5):
p = Process(target=put_pro,args=(q,i+1))
p.start()
#是否收到子进程的数据
print(q.get())
p.join()
print("————————————end—————————————")
#程序入口
if __name__ == "__main__":
#定义一个队列实例
q = Queue()
#启用多进程
get_pro(q)
>>>运行结果
D:\python软件\python.exe D:/_web_project/tetetetetet.py
————————————start————————————
第1个进程添加了一个数据
第2个进程添加了一个数据
第3个进程添加了一个数据
第4个进程添加了一个数据
第5个进程添加了一个数据
————————————end—————————————
Process finished with exit code 0
Manager实现进程间数据共享
from multiprocessing import Manager 数据管理器类,一个管理器类可以创建用于进程间数据共享的各种对象,例如:dict,list,queue,Array,Lock等,有了管理器就可以根据需要定义数据类型,减少局限性
from multiprocessing import Process,Manager
# 定义两个子进程,分别向全局变量中添加数据
def add_one(d,ls,n):
d["name%s" %n] = "进程一%s" % n
ls.append(n)
print("进程1执行了",d,ls)
def add_two(d,ls,n):
d["name%s" %n] = "进程二%s" % n
ls.append(n)
print("进程2执行了",d,ls)
def run(d,ls):
for i in range(3):
p1 = Process(target=add_one,args=(d,ls,i))
p1.start()
p1.join()
p2 = Process(target=add_two,args=(d,ls,i))
p2.start()
p2.join()
#输出执行完毕的全局变量
print(d,ls)
if __name__ == "__main__":
#定义几个全局的Manager变量
d = Manager().dict()
ls = Manager().list()
run(d,ls)
>>>运行结果
D:\python软件\python.exe D:/_web_project/tetetetetet.py
进程1执行了 {'name0': '进程一0'} [0]
进程2执行了 {'name0': '进程二0'} [0, 0]
进程1执行了 {'name0': '进程二0', 'name1': '进程一1'} [0, 0, 1]
进程2执行了 {'name0': '进程二0', 'name1': '进程二1'} [0, 0, 1, 1]
进程1执行了 {'name0': '进程二0', 'name1': '进程二1', 'name2': '进程一2'} [0, 0, 1, 1, 2]
进程2执行了 {'name0': '进程二0', 'name1': '进程二1', 'name2': '进程二2'} [0, 0, 1, 1, 2, 2]
{'name0': '进程二0', 'name1': '进程二1', 'name2': '进程二2'} [0, 0, 1, 1, 2, 2]
Process finished with exit code 0
————————————————————————————————————————————————————————————————————
#如果将程序中的管理器字典和管理器列表变成{}和[],运行结果为
进程1执行了 {'name0': '进程一0'} [0]
进程2执行了 {'name0': '进程二0'} [0]
进程
1执行了 {'name1': '进程一1'} [1]
进程2执行了 {'name1': '进程二1'} [1]
进程1执行了 {'name2': '进程一2'} [2]
进程2执行了 {'name2': '进程二2'} [2]
{} []
python的多进程操作大概就是这样,对于python来说由于cpython解释器全局锁的存在,python的多线程并不能发挥它的威力,熟练掌握多进程是很重要的