关于Python里面协程的一些理解

#关于Python里面协程的一些理解

Python关于协程是有点难理解的,但其实就协程的定义来说其实并不难。

协程,又称微线程,纤程。英文名Coroutine

协程和多线程以及多进程都不一样,协程有极高的执行效率。
因为子程序切换不是线程切换,而是由程序自身控制,
因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。

Python对协程的支持是通过生成器(generator)实现的。

所谓生成器,也是一种可迭代对象,通常创建生成器有两种方式
1、如:g = (x*x for x in range(5))
	   print(g)=====><generator object <genexpr> at 0x0000000002A3CFC0>
	   即g是一个生成器对象
	   操作g有两种方式:
			①next()方法:next(g)==>0 next(g)==>1 next(g)==>4 next(g)==>9 next(g)==>16
			②for循环(上面说到了生成器是可以迭代的):
				for i in g:
					print(i)
				输出是:0 1 4 9 16

2、函数里面包含关键字yield就成了生成器
	如:def A():
			for i in range(3):
				yield i
		这里值得注意的是它与一般函数执行过程不大一样,遇到yield就返回,
		它返回的不是具体的值而是一种状态值,然后下一次从yield下面的代码开始执行
		操作A也是两种,一种next一种for循环。
		需要注意下下面的代码:
		>>> def a():
		...     yield 3
		...
		>>> a()
		<generator object a at 0x0000000002A3CF68>
		>>> type(a)
		<class 'function'> 可以看到这里a还是方法
		>>> type(a())		但是a()是生成器
		<class 'generator'>
了解了生成器后便来聊聊协程吧,我们通过一个例子来理解:
	
传统的生产者-消费者模型是一个线程写消息,一个线程取消息,
通过锁机制控制队列和等待,但一不小心就可能死锁。

如果改用协程,生产者生产消息后,直接通过yield跳转到消费者开始执行,
待消费者执行完毕后,切换回生产者继续生产,效率极高:	
def consumer():
    r = ''
    while True:
        n = yield r
        if not n:
            return
        print('consumer %s' %n)
        r = '886'
def produce(c):
    c.send(None)
    i = 0
    while i < 5:
        i = i+1
        print('produce %s' %i)
        m = c.send(i)
        print('consumer66660 %s' %m)
    c.close()
c = consumer()
produce(c)

输出结果:
produce 1
consumer 1
consumer66660 886
produce 2
consumer 2
consumer66660 886
produce 3
consumer 3
consumer66660 886
produce 4
consumer 4
consumer66660 886
produce 5
consumer 5
consumer66660 886

上面这段代码,consumer是一个生成器,而produce函数传入的参数是一个生成器对象。
代码的具体执行顺序是最需要弄明白的
具体流程:
1、首先c = consumer()创建了一个生成器对象c
	注意这里创建生成器对象c的时候,并没有去执行consumer内部的代码,
	就比如下面这段代码:
		>>> def A():
		...     for i in range(7):
		...         print(666)
		...         yield i
		...
		>>> n = A()
		>>> next(n)
		666
		0				
	可以看到创建n的时候并没有打印出666,证明没有执行内部代码。

2、然后执行produce(c)函数,这里首先执行c.send(None),这个send(None)方法相当于启动了生成器consumer
	注意这里send(None)参数必须是None,
	因为send函数是将参数赋值给类似consumer里面的n = yield r 中的n的,
	而这里根本还没有n存在,你如果传个非空的参数进去就会报错。
	
3、然后转而执行consumer内代码,首先r=‘’,然后进入while循环,执行到n = yield r句的时候我们要特别注意:
		其实这行代码包含了三个意思:
		①向函数外抛出(返回)r
		②暂停,等待next()或send()恢复
		③赋值,n接收send(m)发送进来的m 
	这样对应起来,此时执行到这里,向函数外抛出r(这里但是并没有谁去接收这个r)之后,然后函数暂停。
4、转而执行produce()里的语句i = 0,然后继续向下执行,到print('produce %s' %i)语句
	打印出第一条输出:produce 1。
5、继续向下执行遇到 m = c.send(i),再次切换到consumer内执行,
	这里切换过去后要注意,是从n = yield r语句的第③步开始的,
	即将m = c.send(i)(此时i为1)的i传给了n = yield r里面的n(所以此时n为1)
6、继续向下执行consumer里面的代码,if not n 判断条件 not n为false,跳过if语句
	执行print('consumer %s' %n),输出consumer 1,然后r = ‘886‘,然后继续while循环
	 又遇到n = yield r,此时抛出的r,然后暂停。
	 注意:这个抛出的r(已经变为886)被produce内m = c.send(i)的m接受了于是m=’886‘
7、然后执行print('consumer66660 %s' %m) 输出:consumer66660 886
		至此,有了前三行输出:
			produce 1
			consumer 1
			consumer66660 886
8、然后进入produce里面第二次while循环,流程跟上面一样,不再赘述。

以上,不足之处请多多指教,Thanks~! 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值