前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >多任务之多进程和协程实现

多任务之多进程和协程实现

作者头像
不断折腾
发布2019-09-23 10:47:49
8420
发布2019-09-23 10:47:49
举报
文章被收录于专栏:折腾折腾再折腾

多进程

进程是什么?

上一篇我们了解了多线程,多线程实现了多任务。

那么多进程就是多任务另一种实现方式。

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、多线程和多进程根据电脑的不同,有可能是并行的,但是协程一定是并发。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-12-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 python入门到放弃 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档