联系我们 - 广告服务
您的当前位置:官网首页 > 资讯 > 时局 >

5.并发编程协程

来源: 编辑: 时间:2019-09-06
导读:

io密集型 串行与协程的对比

二:第一种情况的切换。在任务一遇到io情况下,切到任务二去执行,这样就可以利用任务一阻塞的时间完成任务二的计算,效率的提升就在于此。

import time
def func1:
 while true:
 print
 yield
def func2:
 g=func1
 for i in range:
 next
 time.sleep
 print
start=time.time
func2
stop=time.time
print
yield不能检测io,实现遇到io自动切换

yield不能检测io,实现遇到io自动切换

协程就是告诉cpython解释器,你不是nb吗,不是搞了个gil锁吗,那好,我就自己搞成一个线程让你去执行,省去你切换线程的时间,我自己切换比你切换要快很多,避免了很多的开销,对于单线程下,我们不可避免程序中出现io操作,但如果我们能在自己的程序中控制单线程下的多个任务能在一个任务遇到io阻塞时就切换到另外一个任务去计算,这样就保证了该线程能够最大限度地处于就绪态,即随时都可以被cpu执行的状态,相当于我们在用户程序级别将自己的io操作最大限度地隐藏起来,从而可以迷惑操作系统,让其看到:该线程好像是一直在计算,io比较少,从而更多的将cpu的执行权限分配给我们的线程。

协程的本质就是在单线程下,由用户自己控制一个任务遇到io阻塞了就切换另外一个任务去执行,以此来提升效率。为了实现它,我们需要找寻一种可以同时满足以下条件的解决方案:

#1. 可以控制多个任务之间的切换,切换之前将任务的状态保存下来,以便重新运行时,可以基于暂停的位置继续执行。
#2. 作为1的补充:可以检测io操作,在遇到io操作的情况下才发生切换

协程:是单线程下的并发,又称微线程,纤程。英文名coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。、

需要强调的是:

#1. python的线程属于内核级别的,即由操作系统控制调度
#2. 单线程内开启协程,一旦遇到io,就会从应用程序级别控制切换,以此来提升效率

对比操作系统控制线程的切换,用户在单线程内控制协程的切换

优点如下:

#1. 协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级
#2. 单线程内就可以实现并发的效果,最大限度地利用cpu

缺点如下:

#1. 协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程
#2. 协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程

总结协程特点:

如果我们在单个线程内有20个任务,要想实现在多个任务之间切换,使用yield生成器的方式过于麻烦,而使用greenlet模块可以非常简单地实现这20个任务直接的切换

#安装
pip3 install greenlet

#真正的协程模块就是使用greenlet完成的切换

from greenlet import greenlet
def eat:
 print #2
 g2.switch #3
 print #6
 g2.switch #7
def play:
 print #4
 g1.switch #5
 print #8
g1=greenlet
g2=greenlet
g1.switch#可以在第一次switch时传入参数,以后都不需要 1

单纯的切换,反而会降低程序的执行速度

#顺序执行
import time
def f1:
 res=1
 for i in range:
 res+=i
def f2:
 res=1
 for i in range:
 res*=i
start=time.time
stop=time.time
print) #10.985628366470337
from greenlet import greenlet
import time
def f1:
 res=1
 for i in range:
 res+=i
 g2.switch
def f2:
 res=1
 for i in range:
 res*=i
 g1.switch
start=time.time
g1=greenlet
g2=greenlet
g1.switch
stop=time.time
print) # 52.763017892837524
效率对比

效率对比

greenlet只是提供了一种比generator更加便捷的切换方式,当切到一个任务执行时如果遇到io,那就原地阻塞,仍然是没有解决遇到io自动切换来提升效率的问题。

img

img

上面这个图,是协程真正的意义,虽然没有规避固有的i/o时间,但是我们使用这个时间来做别的事情了,一般在工作中我们都是进程+线程+协程的方式来实现并发,以达到最好的并发效果,如果是4核的cpu,一般起5个进程,每个进程中20个线程,每个线程可以起500个协程,大规模爬取页面的时候,等待网络延迟的时间的时候,我们就可以用协程去实现并发。 并发数量 = 5 * 20 * 500 = 50000个并发,这是一般一个4cpu的机器最大的并发数。nginx在负载均衡的时候最大承载量就是5w个

单线程里的这20个任务的代码通常会既有计算操作又有阻塞操作,我们完全可以在执行任务1时遇到阻塞,就利用阻塞的时间去执行任务2。。。。如此,才能提高效率,这就用到了gevent模块。

#安装
pip3 install gevent

gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是greenlet, 它是以c扩展模块形式接入python的轻量级协程。 greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。

#用法
g1=gevent.spawn创建一个协程对象g1,spawn括号内第一个参数是函数名,如eat,后面可以有多个参数,可以是位置实参或关键字实参,都是传给函数eat的,spawn是异步提交任务
g2=gevent.spawn
g1.join #等待g1结束
g2.join #等待g2结束 有人测试的时候会发现,不写第二个join也能执行g2,是的,协程帮你切换执行了,但是你会发现,如果g2里面的任务执行的时间长,但是不写join的话,就不会执行完等到g2剩下的任务了
#或者上述两步合作一步:gevent.joinall
g1.value#拿到func1的返回值

遇到io阻塞时会自动切换任务

import gevent
def eat:
 print
 gevent.sleep
 print
def play:
 print
 gevent.sleep
 print
责任编辑:

打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

上一篇:分布式任务队列
下一篇:没有了

网友评论:

在“\templets\demo\comments.htm”原来的内容全部删除,插入第三方评论代码,如果不需要评论功能,删除comments.html里面的内容即可
推荐使用友言、多说、畅言(需备案后使用)等社会化评论插件

Copyright © 2018 k8凯发国际娱乐k8凯发国际娱乐-k8凯发国际娱乐下载-k8凯发官方 All Rights Reserved Power by DedeCms
本站所有资讯来源于网络 如有侵权请联系QQ:9490489
Top