多进程
进程是什么?
上一篇我们了解了多线程,多线程实现了多任务。
那么多进程就是多任务另一种实现方式。
wondows在任务管理器中可以看到很多进程,这是我们一个程序运行之后的结果。
只有程勋运行起来才可以调度我们的资源,比如qq调用我们的摄像头。
说白了进程是操作系统分配资源的基本单位。
进程的状态
1、就绪态:条件都已经满足,等待cpu执行。
2、执行态:cpu正在执行
3、等待态:等待其他条件的满足
利用进程实现多任务
实例:
还是上一篇文章的例子:
import time
# 导入多进程模块
import multiprocessing
def movietheaters():
for i in range(3):
print('我在看电影')
time.sleep(1)
def foodGotEaten():
for i in range(3):
print('我在吃爆米花')
time.sleep(1)
def main():
# 实例一个多进程对象
p1 = multiprocessing.Process(target = movietheaters)
p2 = multiprocessing.Process(target = foodGotEaten)
# 创建一个进程
p1.start()
p2.start()
if __name__ == "__main__":
main()
这就使用进程实现了多进程。当我们start()的时候,相当于复制了一份相同的代码在子进程只是该进程只执行他该执行的代码(主语是相当于,其实不是这样,进程当然有他的优化方式,能不复制的就不复制,一般只有修改的时候才会拷贝,比线程占用的资源大)。由于主进程有的,子进程也有,占用的资源就多。效率就低了。但是在浪费资源(内存)的时候,提高了效率。
进程和线程的区别
二者的关系
现有进程才有线程,当我们把一个程序运行起来叫做进程,在一个进程里一定有一个主线程。
多线程是在一个进程里写多个线程,而多进程是多个进程里每个进程都运行一个主线程,进程多了,主线程也就多了,也就实现了多任务。通俗点相当于一个苹果2个人分,和再拿一个苹果,一人一个。当人多了,一个苹果不够分了,那就多拿几个苹果。
区别
1、线程不能独立运行,必须存在进程中。
2、使用都有自己优缺点,线程执行占用资源少,不利于资源管理和保护,同理进程相反
3、进程拥有独立的内存单元,多线程共享内存。
进程间如何传递值
上面说了,多进程是独立的内存,那怎么办?(用全局变量不行)
可以通过进程间的通讯来解决,比如socket。也可以用文件储存,一个存一个取。
但是这里不用上面的方法,我们用Queue队列(数据特性:先进先出。栈:刚好相反,先进后出)。
Queue队列是什么?
相当于一块内存,用来存数据。
如何使用:
# 导入模块
from multiprocessing import Queue
# 实例化一个队列 2表示最多放2条数据,不放数值,默认最大
q = Queue(2)
# 存放一条数据
q.put('我是第一个')
# 查看队列是否存满 是返回True,不是返回False
print(q.full())
q.put('我是第二个')
# 判断队列是否为空,是返回True,不是返回False
print(q.empty())
# 取数据
# 调用一次,只取出一个值,需要for循环取值
print(q.get())
# 当你取出一个数据的时候,队列就不是满的了
# q.qsize()返回放了多少条数据
实例实现:
import multiprocessing
def test1(queue):
list = [1,2,3,4,5]
# test1 的数据传到队列中
for item in list:
queue.put(item)
def test2(queue):
# 创建一个空列表
list2 = []
# 获取队列中的数据
while True:
if queue.empty():
break
data = queue.get()
list2.append(data)
print(list2)
def main():
# 创建队列
q = multiprocessing.Queue()
p1 = multiprocessing.Process(target = test1, args=(q,))
p2 = multiprocessing.Process(target = test2, args=(q,))
p1.start()
p2.start()
if __name__ == "__main__":
main()
这样我们就在test2中引用到了test1的数据。
进程池Pool
进程池是什么?
一个容器,能够容纳很多进程,但是能够重复运用里面的进程。
加入有10个任务,我们用进程池创建2个进程,当着两个用完,再来两个,以次类推。
什么时候进程池?
1、当不确定需要多少个进程时。
2、当需要创建的进程多的时候。
如何创建一个进程池
# 导入模块
from multiprocessing import Pool
import os,time,random
def test(m):
sta_time = time.time()
print('执行第%s个进程,进程号为:%s'%(m,os.getpid()))
time.sleep(random.random()*3)
end_time = time.time()
# 在输出的字符串前加f,{}中可以直接写参数,另一种格式化输出
print(f'执行完毕,耗时{sta_time-end_time}')
def main():
# 创建一个进程池,最大进程为3
p = Pool(3)
for i in range(10):
# 创建一个进程,第一个参数为要调度的函数,第二个为要传递的参数,为元组类型
p.apply_async(test,(i,))
print('开始进程!')
# 关闭进程池
p.close()
# 等待所有进程执行完,需要放在close后
p.join()
# 全部结束!
print('结束进程')
if __name__ == "__main__":
main()
结果:
协程
yield实现:
协程是实现多任务的第三种方式,是占用资源最少的。
还是同一个例子:
import time
# 导入多进程模块
import multiprocessing
def movietheaters():
while True:
print('我在看电影')
time.sleep(1)
yield
def foodGotEaten():
while True:
print('我在吃爆米花')
time.sleep(1)
yield
def main():
m = movietheaters()
f = foodGotEaten()
while True:
next(m)
next(f)
if __name__ == "__main__":
main()
注意:多任务分为并发和并行,并发就是假的,让你以为一时间在执行多个任务,他们只是协调了步伐,你做的时候他不做,他做你不做。并行才是同一时间执行多任务。但是往往大多数都是并发。
以上很麻烦,每次调用都需要写next()和yield
用greenlet、gevent实现多任务
greenlet实例:
首先需要安装greenlet
pip3 install greenlet
代码:
from greenlet import greenlet
import time
def movietheaters():
while True:
print('我在看电影')
gr2.switch()
time.sleep(0.5)
def foodGotEaten():
while True:
print('我在吃爆米花')
gr1.switch()
time.sleep(1)
gr1 = greenlet(movietheaters)
gr2 = greenlet(foodGotEaten)
gr1.switch()
gevent实例:
gevent只有在遇见延时的时候才会切换任务,在实际运用中不需要延时,并且延时需要gevent.sleep,不能用time.sleep。
首先需要安装gevent
pip3 install gevent
代码:
import gevent
def fun1(x):
for i in range(x):
print(f'我是fun1中的{i}')
gevent.sleep(0.5)
def fun2(x):
for i in range(x):
print(f'我是fun2中的{i}')
gevent.sleep(0.5)
g1 = gevent.spawn(fun1,10)
g2 = gevent.spawn(fun2,10)
g1.join()
g2.join()
如果我们还是想要用time.sleep(),不想用他的延时,怎么办?
需要添加一句代码:monkey.patch_all()
import gevent
import time
from gevent import monkey
monkey.patch_all()
def fun1(x):
for i in range(x):
print(f'我是fun1中的{i}')
time.sleep(0.5)
def fun2(x):
for i in range(x):
print(f'我是fun2中的{i}')
time.sleep(0.5)
g1 = gevent.spawn(fun1,10)
g2 = gevent.spawn(fun2,10)
g1.join()
g2.join()
如果我创建的多任务比较多?是不是都要写很多join?
解决:
import gevent
import time
from gevent import monkey
monkey.patch_all()
def fun1(x):
for i in range(x):
print(f'我是fun1中的{i}')
time.sleep(0.5)
def fun2(x):
for i in range(x):
print(f'我是fun2中的{i}')
time.sleep(0.5)
gevent.joinall({
gevent.spawn(fun1,10),
gevent.spawn(fun2,10),
})
注:协程中主要用gevent
进程、线程、协程对比
1、进程是资源分配的单位
2、线程是操作系统调度的单位
3、进程占用资源大
4、线程需要资源一般,效率一般
5、协程需要资源小,效率高
6、多线程和多进程根据电脑的不同,有可能是并行的,但是协程一定是并发。