python中yield,greenlet,gevent实现协程

一.什么是协程:

        协程是python中另外一种实现多任务的方式,只不过比线程更小,占用的执行资源(理解为需要的资源)也更少。为啥说他说一个执行单元?因为他自带cpu上下文,这样,在合适的额gr时机,我们就可以把一个协程切换到另一个协程。只要这个过程中保存或恢复了cpu上下文,那么程序就还是可以运行的。

二.协程与线程的差异:

        在实现多任务时,线程的切换远不止系统层面上的保存和恢复cpu上下文那么简单。操作系统为了程序运行的高效性,每个线程都有自己的缓存cache等数据,操作系统会帮你做这些数据的恢复操作(快照恢复),所以线程的切换相对协程而言更耗性能。但是协程的切换只是单纯地操作cpu上下文,所以一秒钟切换上百万次协程都是没问题的。

python中的高性能框架tornado和性能测试工具locust底层用的就是协程实现。 

1.未通过生成器实现多任务:yield

"""未通过yield关键字利用生成器实现多任务"""
import time


def work1():
    for i in range(5):
        print("-----work1--执行----{}".format(i))
        time.sleep(0.1)


def work2():
    for i in range(5):
        print("-----work2--执行----{}".format(i))
        time.sleep(0.1)


if __name__ == '__main__':
    st = time.time()
    work1()
    work2()
    et = time.time()
    print("运行时间为:{}秒".format(et - st))


输出:
-----work1--执行----0
-----work1--执行----1
-----work1--执行----2
-----work1--执行----3
-----work1--执行----4
-----work2--执行----0
-----work2--执行----1
-----work2--执行----2
-----work2--执行----3
-----work2--执行----4
运行时间为:1.0294520854949951秒

总结:未实现生成器之前,程序运行按顺序执行:work1执行完了再执行work2。

2.通过生成器实现微线程(协程):yield(保存执行的上下文)

        通过yield生成器实现多任务时,work1和work2时交替执行的。

"""通过yield关键字利用生成器实现多任务"""
import time


def work1():
    for i in range(5):
        print("-----work1--执行----{}".format(i))
        yield
        time.sleep(0.1)


def work2():
    for i in range(5):
        print("-----work2--执行----{}".format(i))
        yield
        time.sleep(0.1)



if __name__ == '__main__':
    st = time.time()
    g1 = work1()
    g2 = work2()
    while True:
        try:
            next(g1)
            next(g2)
        except Exception as e:
            print("无可迭代对象")
            break
    et = time.time()
    print("运行时间:{}".format(et - st)) # 4.696846008300781e-05


输出:
-----work1--执行----0
-----work2--执行----0
-----work1--执行----1
-----work2--执行----1
-----work1--执行----2
-----work2--执行----2
-----work1--执行----3
-----work2--执行----3
-----work1--执行----4
-----work2--执行----4
无可迭代对象
运行时间:0.9351921081542969

总结:     1.协程(微线程,Coroutine)的本质是单任务;

                2.协程依赖于线程;

                3.协程相对于线程,占用的资源更少(几乎不占用资源)。

三.greenlet(比较low):

        为了更好的使用协程来实现多任务,python中的greenlet模块对其封装,从而使得切换任务变得更加简单。

"""
协程模块安装:pip install greenlet/pip install gevent
"""
from greenlet import greenlet
import time


def work1():
    for i in range(5):
        print("work1--正在执行:{}".format(i))
        g2.switch()  # 切换到g1
        time.sleep(0.1)


def work2():
    for i in range(5):
        print("work2--正在执行:{}".format(i))
        g1.switch()  # 切换到g2
        time.sleep(0.1)


if __name__ == '__main__':
    st = time.time()
    g1 = greenlet(work1)
    g2 = greenlet(work2)
    # 切换到g1中执行
    g1.switch()  # 执行g1
    print("所有协程执行完毕~~~")
    et = time.time()
    print("运行时间:{}".format(et - st)) 

输出:
work1--正在执行:0
work2--正在执行:0
work1--正在执行:1
work2--正在执行:1
work1--正在执行:2
work2--正在执行:2
work1--正在执行:3
work2--正在执行:3
work1--正在执行:4
work2--正在执行:4
所有协程执行完毕~~~
运行时间:0.930009126663208

四.gevent:(推荐)

        协程存在与线程之中,线程默认不会等待协程执行。
        spawn():开启协程(第一个参数为协程要执行的任务)
        join():线程要等待协程执行结束。
        协程之间切换条件:
            gevent.sleep():耗时等待的情况下才会切换
"""
    协程:gevent(终极版本)
    协程存在与线程之中,线程默认不会等待协程执行。
    spawn():开启协程(第一个参数为协程要执行的任务)
    join():线程要等待协程执行结束。
    协程之间切换条件:
        gevent.sleep():耗时等待的情况下才会切换
"""
import gevent, time


def work1():
    for i in range(5):
        print("work1--正在执行:{}".format(i))
        gevent.sleep(0.1)


def work2():
    for i in range(5):
        print("work2--正在执行:{}".format(i))
        gevent.sleep(0.1)


if __name__ == '__main__':
    st = time.time()
    # 创建2个协程
    g1 = gevent.spawn(work1)
    g2 = gevent.spawn(work2)
    # 线程等待协程执行完毕
    g1.join()
    g2.join()
    et = time.time()
    print("运行时间为:{}秒".format(et - st))

输出:
work1--正在执行:0
work2--正在执行:0
work1--正在执行:1
work2--正在执行:1
work1--正在执行:2
work2--正在执行:2
work1--正在执行:3
work2--正在执行:3
work1--正在执行:4
work2--正在执行:4
运行时间为:0.5262598991394043秒

使用gevent.monkey.patch_all()补丁,可不用gevent.sleep()方法,有耗时的io操作时会自动切换。

"""
    协程:gevent(终极版本)
    协程存在与线程之中,线程默认不会等待协程执行。
    spawn():开启协程(第一个参数为协程要执行的任务)
    join():线程要等待协程执行结束。
    协程之间切换条件:
        gevent.sleep():耗时等待的情况下才会切换
        如果使用了gevent的程序补丁,则不需要gevent.sleep(),会自动切换
        gevent.monkey.patch_all()
"""
import gevent, time
from gevent import monkey

gevent.monkey.patch_all()


def work1():
    for i in range(5):
        print("work1--正在执行:{}".format(i))
        time.sleep(0.1)


def work2():
    for i in range(5):
        print("work2--正在执行:{}".format(i))
        time.sleep(0.1)


if __name__ == '__main__':
    st = time.time()
    # 创建2个协程
    g1 = gevent.spawn(work1)
    g2 = gevent.spawn(work2)
    # 线程等待协程执行完毕
    g1.join()
    g2.join()
    et = time.time()
    print("运行时间为:{}秒".format(et - st))

输出:
work1--正在执行:0
work2--正在执行:0
work1--正在执行:1
work2--正在执行:1
work1--正在执行:2
work2--正在执行:2
work1--正在执行:3
work2--正在执行:3
work1--正在执行:4
work2--正在执行:4
运行时间为:0.518481969833374秒

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chuntian_tester

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值