Python协程

什么是协程?

协程:是单线程下实现并发,又称微线程,纤程。英文名Coroutine。

协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方(线程调度时候寄存器上下文及栈等保存在内存中),在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:

协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。

子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕。

所以子程序调用是通过栈实现的,一个线程就是执行一个子程序。

子程序调用总是一个入口,一次返回,调用顺序是明确的。而协程的调用和子程序不同。

协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。

注意,在一个子程序中中断,去执行其他子程序,不是函数调用,有点类似CPU的中断。比如子程序A、B:

def A():
    print( "1")
    print("2")
    print("3")

def B():
    print("x")
    print("y")
    print("z")

A()
B()

输出结果:

1
2
3
x
y
z

假设由协程执行,在执行A的过程中,可以随时中断,去执行B,B也可能在执行过程中中断再去执行A,结果就可能是:

1
x
2
y
3
z

但是在A中是没有调用B的,所以协程的调用比函数调用理解起来要难一些。
看起来A、B的执行有点像多线程,但协程的特点在于是一个线程执行。

一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。

需要强调的是:
        1. python的线程属于内核级别的,即由操作系统控制调度(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其他线程运行)
        2. 单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操作系统)控制切换,以此来提升效率(强调:不是io操作的切换与效率无关)

协程的优点

        优点1: 协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因 此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。
        优点2: 不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中 控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。 因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利 用多核,又充分发挥协程的高效率,可获得极高的性能。

yield的简单实现

"""
@Time :2020/2/13 16:11
@Coding :UTF-8 
@Auth :Bing.
@IDE :PyCharm
@File :18.协程—yield实现.py
@Motto:Move On
"""
import time

def consumer(name):
    """
    这是一个生成器,调用的时候才执行
    :param name: 消费者的名字
    :return: 包子
    """

    print("消费者准备吃包子------")
    while True:
        new_baozi = yield  # 接受生成器的值
        print("%s吃第%d个包子"%(name,new_baozi))
        time.sleep(1)

def producer(name):

    r=con1.__next__()
    r=con2.__next__()

    count=1
    while True:
        print("%s正在生产第%d个包子和第%d个包子"%(name,count,count+1))
        # 调用生成器并且发送数据
        con1.send(count)
        con2.send(count+1)
        count+=2

if __name__ == '__main__':
    con1=consumer("翠花")
    con2=consumer("王大锤")
    p=producer("大厨")

greenlet模块

Greenlet是python的一个C扩展,来源于Stackless python,旨在提供可自行调度的‘微线程’, 即协程。generator实现的协程在yield value时只能将value返回给调用者(caller)。 而在greenlet中,target.switch(value)可以切换到指定的协程(target), 然后yield value。greenlet用switch来表示协程的切换,从一个协程切换到另一个协程需要显式指定。是一种手动切换,后面会介绍能实现自动切换的Gevent

安装greenlet

pip install greenlet

greenlet实例

"""
@Time :2020/2/13 16:39
@Coding :UTF-8 
@Auth :Bing.
@IDE :PyCharm
@File :19.协程—greenlet.py
@Motto:Move On
"""
from greenlet import greenlet

def work1():
    print(12)
    # 3.切换到test2()函数中
    gr2.switch()
    print(45)
    gr2.switch()

def work2():
    print(89)
    gr1.switch()
    print(43)

# 1.将要执行的函数封装到greenlet对象中
gr1=greenlet(work1)
gr2=greenlet(work2)
# 2.想先执行那个函数就可以使用   对象.swith()方法进行执行
gr1.switch()

gevent模块

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

当一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。
安装gevent

pip install gevent

gevent实例

"""
@Time :2020/2/13 17:08
@Coding :UTF-8 
@Auth :Bing.
@IDE :PyCharm
@File :20.协程—gevent.py
@Motto:Move On
"""
import time
import requests
import gevent

def f(url):
    print("get",url)
    resp=requests.get(url)
    data=resp.text
    print("%d bytes received from %s"%(len(data),url))

# 1.普通模式
s = time.time()
f("http://langlang2017.com/img/banner1.png")
f("http://langlang2017.com/img/banner2.png")
f("http://langlang2017.com/img/banner3.png")
f("http://langlang2017.com/img/banner4.png")
e = time.time()
print("普通模式时间:",e-s)

# 2.使用gevet模块
start = time.time()
gevent.joinall(
[gevent.spawn(f,"http://langlang2017.com/img/banner1.png"), # 创建一个普通的greenlet对象并切换
gevent.spawn(f,"http://langlang2017.com/img/banner2.png"), # 创建一个普通的greenlet对象并切换
gevent.spawn(f,"http://langlang2017.com/img/banner3.png"), # 创建一个普通的greenlet对象并切换
gevent.spawn(f,"http://langlang2017.com/img/banner4.png"),] # 创建一个普通的greenlet对象并切换
)
print("gevent时间:",time.time()-start)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值