分享

python 基础(五)协程 —— 微线程 greenlet gevent

 印度阿三17 2020-01-31

python基础系列 正在持续更新中:)

文章目录

回忆进程与线程 引入

我们之前说,有比较耗时的操作得交给线程来干,因为进程是同步调用,阻塞执行,串行执行的,那么我们就要用线程,异步调用,非阻塞并行执行,这样的话才能提升效率。
问题来了,假设线程中也有几个子任务,耗时,然后我们程序都是串行的,也就一个Thread里面的run()都是串行执行的,一个子任务,比方下载很慢,其他任务都阻塞在那,就效率又很低了。
这时的线程就好像以前的进程一样尴尬。
我们python就出来一个微线程,可以并行操作的,线程的子任务部分,也称为
携程
协程(Coroutine) cor - routine 也可以看出并行的含义

比如,我们有很多任务要做,打游戏,学习python,配朋友,这就是三个进程。在打游戏中,我有 黑暗之魂1,2,3,上古卷轴5,也就是4个线程,当然,上古卷轴5有100个mod,我需要更新100个mod,就有了100个协程。因为更新比较耗时,必须并行调用 更新mod的任务。

greenlet

python封装了库greenlet来实现协程,我们来看个例子:

#-*- utf-8 -*-
from time import sleep,time
from greenlet import greenlet
def taskA():
    for i in range(5):
        print('A' str(i))
        gB.switch()
        sleep(1)  # 模拟网络阻塞 耗时操作


def taskB():
    for i in range(5):
        print('B'   str(i))
        gC.switch()
        sleep(1)  # 模拟网络阻塞 耗时操作



def taskC():
    for i in range(5):
        print('C'   str(i))
        gA.switch()
        sleep(1)  # 模拟网络阻塞 耗时操作


if __name__ == "__main__":
    t1 = time()
    gA = greenlet(run = taskA)
    gB = greenlet(run = taskB)
    gC = greenlet(run = taskC)

    gA.switch()
    
    t2 = time()
    print("time consume",t2-t1)

类似process thread, greenlet也必须传target参数(他命名为run参数 反正一个意思)
这里主要是实现A B C并行轮询,但是我们可以看到这样写很蠢,需要人工切换,而且效率也不算高,于是官方的greenlet不好用,就有大佬弄出来gevent库(第三方),在greenlet基础上再封装一波,可以实现自动切换。

gevent

利用spawn函数传要做的事(taskA B C),spawn有 产卵; 引发; 引起 这几个意思,有点run的含义。
注意,我们之前在进程里面遇到过,主进程启动完子进程start以后就跑路了,导致子进程也gg,这样我们就用join阻塞,让主进程别溜,这里也是同样的道理。

#-*- utf-8 -*-
from time import sleep,time
from gevent import monkey,spawn

def taskA():
    for i in range(3):
        print('A' str(i))
        sleep(1)  # 模拟网络阻塞 耗时操作


def taskB():
    for i in range(3):
        print('B'   str(i))
        sleep(1)  # 模拟网络阻塞 耗时操作



def taskC():
    for i in range(3):
        print('C'   str(i))
        sleep(1)  # 模拟网络阻塞 耗时操作


if __name__ == "__main__":
    t1 = time()
    gA = spawn(run = taskA)
    gB = spawn(run = taskB)
    gC = spawn(run = taskC)

    gA.join()
    gB.join()
    gC.join()
    t2 = time()
    print("time consume",t2-t1)

结果:

A0
A1
A2
B0
B1
B2
C0
C1
C2
time consume 9.041131258010864

我们发现,
1: 并没有轮询ABC ABC 这样的啊
2: 并没有并行啊2333 其实上面的greenlet也没有实现并行??? 还是9s

monkey

我们的time不适合这个场合的,于是利用monkey.patch_all()

#-*- utf-8 -*-
import time
from gevent import monkey,spawn
monkey.patch_all()

def taskA():
    for i in range(3):
        print('A' str(i))
        time.sleep(1)  # 模拟网络阻塞 耗时操作


def taskB():
    for i in range(3):
        print('B'   str(i))
        time.sleep(1)  # 模拟网络阻塞 耗时操作



def taskC():
    for i in range(3):
        print('C'   str(i))
        time.sleep(1)  # 模拟网络阻塞 耗时操作


if __name__ == "__main__":
    t1 = time.time()
    gA = spawn(run = taskA)
    gB = spawn(run = taskB)
    gC = spawn(run = taskC)

    gA.join()
    gB.join()
    gC.join()
    t2 = time.time()
    print("time consume",t2-t1)

这里 必须time.sleep才能生效 因为monkey在背后偷偷重载替换了原生的sleep,你直接from time import sleep, monkey匹配不到
结果:

A0
B0
C0
A1
B1
C1
A2
B2
C2
time consume 3.036893606185913

Process finished with exit code 0

可见,比较完美实现了并行,time consume 时间消耗 从9s 并行变成了 3s

总结

我们直接用gevent monkey就行了 其他的执行方式太麻烦了,简直就是再造轮子:)

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多