深入理解yield(二):yield与协程
转自:http://blog.beginman.cn/blog/133/
协程概念
1.并发编程的种类:多进程,多线程,异步,协程
2.进程,线程,协程的概念区别:
进程:拥有自己独立的堆和栈,既不共享堆也不共享栈,进程由操作系统调度。
线程:线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度(标准线程是的)。
协程:协程和线程一样共享堆,不共享栈,协程由程序员在协程的代码里显式调度
3.协程:
协程可以认为是一种用户态的线程,与系统提供的线程不同点是,它需要主动让出CPU时间,而不是由系统进行调度,即控制权在程序员手上。
4.进程,线程,协程在python中的表现
关于进程,python有multiprocessing
模块进行处理;关于线程,python的Thread
和Threading
处理;关于异步,有各种python框架如Tornado等调用linux下的select
,poll
,epoll
等异步实现;关于协程,由yield
来实现。
5.协程与yield
在廖雪峰的python博客中学习总结协程如下:
1).子程序调用通过栈实现,一个线程执行一个子程序,有明确的执行顺序,而协程不同于此,协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。注意的一点是:如A,B两个子程序, A执行过程中可以去执行B,并不是调用B,B可以中断(挂起)转而回到A.
所以可以看到执行过程在A,B两个子程序中切换,但是A并没有调用B,B的中断可以通过yield
来保存,A,B的执行过程像多线程,但是协程的特点在于一个线程执行。
2).协程的优势:
最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。
第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。
下面通过多线程的生产者消费者与协程的生产者消费者实例进行演示:
# 多线程
# coding=utf-8
from threading import Thread
from threading import Condition
import time
import random
#为了达到理解效果,这里没有使用简便的Queue
queue = []
condition = Condition()
MAX_LENGTH = 20
#代码实例:http://blog.jobbole.com/52412/
#生产者的工作是产生一块数据,放到buffer中,如此循环。
#与此同时,消费者在消耗这些数据(例如从buffer中把它们移除),每次一块。
#这里的关键词是“同时”。所以生产者和消费者是并发运行的,我们需要对生产者和消费者做线程分离。
#这个为描述了两个共享固定大小缓冲队列的进程,即生产者和消费者。
class ProducerThread(Thread):
def run(self):
nums = range(5)
global queue
while True:
num = random.choice(nums)
condition.acquire() # 锁定以生成数据
if len(queue) == MAX_LENGTH:
print u'队列已满,等待消费中...'
condition.wait() # 线程等待
queue.append(num) # 往队列中生成数据
print "Produced", num
condition.notify() # 通知线程等待的消费者消费数据
condition.release() # 释放线程锁
time.sleep(random.random())
class ConsumerThread(Thread):
def run(self):
global queue
while True:
condition.acquire() #线程锁
if not queue:
print u"队列为空,等待生产者生成数据...."
condition.wait() # 线程等待
num = queue.pop(0) # 消费数据
print "Consumed", num
condition.notify() # 通知线程等待的生产者继续生成数据(生产者线程等待条件是队列已满)
condition.release() # 释放线程锁
time.sleep(2) #执行时间长点,让生产者生成更多数据来测试
ProducerThread().start()
ConsumerThread().start()
传统的生产者-消费者模型是一个线程写消息,一个线程取消息,通过锁机制控制队列和等待,但一不小心就可能死锁。
下面改用协程的方式:
# 协程
#生产者生产消息后,直接通过yield跳转到消费者开始执行,待消费者执行完毕后,切换回生产者继续生产,效率极高:
def consumer():
v = None
while 1:
m = yield v
if not m:
return
print "[C]CLine:%s, CProduce:%s" % (m, v)
v = 'range'
def producter():
i = 0
c = consumer()
c.next() # To avoid sending a non-None value to a just-started generator, you need to call next or send(None) first.
while i < 5:
try:
i += 1
print(u'[P]producing...%s' % i)
v = c.send(i)
print(u'[P]CReturn: %s' % v)
except StopIteration:
print 'Done!'
break
c.close()
if __name__ == '__main__':
producter()
执行结果如下:
[P]producing...1
[C]CLine:1, CProduce:None
[P]CReturn: range
[P]producing...2
[C]CLine:2, CProduce:range
[P]CReturn: range
[P]producing...3
[C]CLine:3, CProduce:range
[P]CReturn: range
[P]producing...4
[C]CLine:4, CProduce:range
[P]CReturn: range
[P]producing...5
[C]CLine:5, CProduce:range
[P]CReturn: range
引用廖雪峰python协程的理解:
注意到consumer函数是一个generator(生成器),把一个consumer传入produce后:
首先调用c.next()启动生成器;
然后,一旦生产了东西,通过c.send(n)切换到consumer执行;
consumer通过yield拿到消息,处理,又通过yield把结果传回;
produce拿到consumer处理的结果,继续生产下一条消息;
produce决定不生产了,通过c.close()关闭consumer,整个过程结束。
整个流程无锁,由一个线程执行,produce和consumer协作完成任务,所以称为“协程”,而非线程的抢占式多任务。
最后套用Donald Knuth的一句话总结协程的特点:
“子程序就是协程的一种特例。”
执行动态图如下:
下节要点:
1.Python异步与yield的总结
2.Tornado异步非阻塞实例与yield的总结
作者:BeginMan
网址: http://blog.beginman.cn/blog/133/
在未标明转载的情况下,本文版权归作者,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
深入理解yield(二):yield与协程的更多相关文章
- 深入理解协程(二):yield from实现异步协程
原创不易,转载请联系作者 深入理解协程分为三部分进行讲解: 协程的引入 yield from实现异步协程 async/await实现异步协程 本篇为深入理解协程系列文章的第二篇. yield from ...
- yield与send实现协程操作
yield与send实现协程操作 之前我们说过,在函数内部含有yield语句即称为生成器. 下面,我们来看看在函数内部含有yield语句达到的效果.首先,我们来看看以下代码: def foo(): w ...
- PHP下的异步尝试二:初识协程
PHP下的异步尝试系列 如果你还不太了解PHP下的生成器,你可以根据下面目录翻阅 PHP下的异步尝试一:初识生成器 PHP下的异步尝试二:初识协程 PHP下的异步尝试三:协程的PHP版thunkify ...
- Kotlin Coroutine(协程): 二、初识协程
@ 目录 前言 一.初识协程 1.runBlocking: 阻塞协程 2.launch: 创建协程 3.Job 4.coroutineScope 5.协程取消 6.协程超时 7.async 并行任务 ...
- yield、greenlet与协程gevent
yield 在说明yield之前,我们了解python中一些概念. 在了解Python的数据结构时,容器(container).可迭代对象(iterable).迭代器(iterator).生成器(ge ...
- Python用yield form 实现异步协程爬虫
很古老的用法了,现在大多用的aiohttp库实现,这篇记录仅仅用做个人的协程底层实现的学习. 争取用看得懂的字来描述问题. 1.什么是yield 如果还没有怎么用过的话,直接把yield看做成一种特殊 ...
- PHP yield 分析,以及协程的实现,超详细版(上)
参考资料 http://www.laruence.com/2015/05/28/3038.html http://php.net/manual/zh/class.generator.php http: ...
- python中和生成器协程相关的yield之最详最强解释,一看就懂(一)
yield是python中一个非常重要的关键词,所有迭代器都是yield实现的,学习python,如果不把这个yield的意思和用法彻底搞清楚,学习python的生成器,协程和异步io的时候,就会彻底 ...
- python协程--yield和yield from
字典为动词“to yield”给出了两个释义:产出和让步.对于 Python 生成器中的 yield 来说,这两个含义都成立.yield item 这行代码会产出一个值,提供给 next(...) 的 ...
- 再议Python协程——从yield到asyncio
协程,英文名Coroutine.前面介绍Python的多线程,以及用多线程实现并发(参见这篇文章[浅析Python多线程]),今天介绍的协程也是常用的并发手段.本篇主要内容包含:协程的基本概念.协程库 ...
随机推荐
- PAT 列车厢调度 (25分)(栈和容器的简单应用)
1 ====== <--移动方向 / 3 ===== \ 2 ====== -->移动方向 大家或许在某些数据结构教材上见到过“列车厢调度问题”(当然没见过也不要紧).今天,我们就来实际操 ...
- HDU 4585
http://acm.hdu.edu.cn/showproblem.php?pid=4585 从原来的人中找出战斗数值最接近的,输出他们两人的序号 要在logn的复杂度完成查找,我用的是set,当然用 ...
- pycharm 提示性信息
语法错误:文字底部红色波浪线 解决方案:语法修改正确 语法不符合规范:文字底部灰色波浪线 解决方案:快捷键(Alt + Enter + Enter ) 单词拼写提示:文字底部绿色波浪线 解决方案: 单 ...
- ListBox item Sort
将Rss内容读取到Listbox control中, 然后实现按照标题或发布日期进行排序. private void ListItemSort(string type) { if (type == & ...
- 使用自己的域名解析cnblogs博客(CSDN也可以)
本文主要介绍怎样使用自己购买的域名指向cnblogs博客 通常来说技术人员都会创建个自己的技术博客,总结下工作中的问题,经验等等,不过某些博客的访问链接的确是不太容易记忆或者输入,对我们分享造成一定的 ...
- Java第十次作业--多线程
一.学习要点 认真看书并查阅相关资料,掌握以下内容: 理解进程和线程的区别 掌握Java多线程的两种实现方式和区别 理解线程对象的生命周期 熟悉线程控制的基本方法 掌握Java线程的同步机制 理解多线 ...
- [Machine Learning & Algorithm] 随机森林(Random Forest)-转载
作者:Poll的笔记 博客出处:http://www.cnblogs.com/maybe2030/ 阅读目录 1 什么是随机森林? 2 随机森林的特点 3 随机森林的相关基础知识 4 随机森林的生成 ...
- HDU 4135:Co-prime(容斥+二进制拆分)
Co-prime Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total S ...
- 生成html报告并整合自动发动邮件功能
from HTMLTestRunner import HTMLTestRunnerfrom email.mime.text import MIMETextfrom email.header impor ...
- (4)socket的基础使用(基于TCP协议的并发编程)
需要实现并发需要依靠socketserver 模块 socketserver模块下有几个功能 def __init__(self, request, client_address, server): ...