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

线程是系统级别的它们由操作系统调度,而协程则是程序级别的由程序根据需要自己调度。在一个线程中会有很多函数,我们把这些函数称为子程序,在子程序执行过程中可以中断去执行别的子程序,而别的子程序也可以中断回来继续执行之前的子程序,这个过程就称为协程。也就是说在同一线程内一段代码在执行过程中会中断然后跳转执行别的代码,接着在之前中断的地方继续开始执行,类似与yield操作。

协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。

协程,英文名是 Coroutine, 又称为微线程,是一种用户态的轻量级线程。协程不像线程和进程那样,需要进行系统内核上的上下文切换,协程的上下文切换是由程序员决定的。在 Python 中协程就是一个可以暂停执行的函数,听起来和生成器的概念一样。

协程的优点:

  (1)无需线程上下文切换的开销,协程避免了无意义的调度,由此可以提高性能(但也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力)

  (2)无需原子操作锁定及同步的开销

  (3)方便切换控制流,简化编程模型

  (4)高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。

协程的缺点:

  (1)无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。

  (2)进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序

yield

def test():
for i in 'abc':
yield i
for i in [1, 2, 3]:
yield i if __name__ == '__main__':
gen = test()
print(gen.send(None))
print(gen.send(None))
print(gen.send(None))
print(gen.send(None))
print(gen.send(None))
print(gen.send(None))
print(gen.send(None))
def test():
for i in 'abc':
yield i
for i in [1, 2, 3]:
yield i if __name__ == '__main__':
gen = test()
for i in gen:
print(i)

使用for循环生成器,因为for循环可以捕捉StopIteration异常。

yield from

上面的代码也可以用yield from实现

def test():
yield from 'abc'
yield from [1, 2, 3] if __name__ == '__main__':
gen = test()
for i in test():
print(i)

yield from 后面需要加的是可迭代对象,它可以是普通的可迭代对象,也可以是迭代器,甚至是生成器。

使用yield from实现实时计算器

def average_gen():
"""
子生成器
"""
average = 0
total = 0
count = 0
while True:
num = yield average
if num is None:
break
count += 1
total += num
average = total / count
return average, count, total def proxy_gen():
"""
委派生成器
"""
while True:
average, count, total = yield from average_gen()
print(f'平均值{average}, 计算{count}次, 总和{total}') def client():
"""
调用端
"""
calc_average = proxy_gen()
calc_average.send(None)
# next(calc_average)
# 预激活协程
print(calc_average.send(10))
print(calc_average.send(20))
print(calc_average.send(30))
calc_average.send(None)
# 关闭协程 if __name__ == '__main__':
client()

在委派生成器中的while True可以替换成for循环,循环的大小决定调用端可以calc_average.send(None)的次数(第一次预激活也计入)。

while Truefor循环用来捕捉StopIteration异常,可以查看第一段代码。

示例

def average_gen():
"""
子生成器
"""
average = 0
total = 0
count = 0
while True:
num = yield average
if num is None:
break
count += 1
total += num
average = total / count
return average, count, total def proxy_gen():
"""
委派生成器
"""
for i in range(2):
average, count, total = yield from average_gen()
print(f'平均值{average}, 计算{count}次, 总和{total}') def client():
"""
调用端
"""
calc_average = proxy_gen()
calc_average.send(None)
# next(calc_average)
# 预激活协程
print(calc_average.send(10))
print(calc_average.send(20))
print(calc_average.send(30))
calc_average.send(None)
# 关闭协程
print(calc_average.send(10))
print(calc_average.send(20))
print(calc_average.send(30))
calc_average.send(None) if __name__ == '__main__':
client()

示例最后会抛出StopIteration异常。

因为在第二次calc_average.send(None)时,for循环捕捉了一次异常,进入了最后一次循环,在calc_average.send(None)时,for不能再次循环,不能处理StopIteration异常,故抛出。

Python协程一点理解的更多相关文章

  1. Python协程深入理解

    从语法上来看,协程和生成器类似,都是定义体中包含yield关键字的函数.yield在协程中的用法: 在协程中yield通常出现在表达式的右边,例如:datum = yield,可以产出值,也可以不产出 ...

  2. Python协程深入理解(转)

    原文:https://www.cnblogs.com/zhaof/p/7631851.html 从语法上来看,协程和生成器类似,都是定义体中包含yield关键字的函数.yield在协程中的用法: 在协 ...

  3. Python 协程总结

    Python 协程总结 理解 协程,又称为微线程,看上去像是子程序,但是它和子程序又不太一样,它在执行的过程中,可以在中断当前的子程序后去执行别的子程序,再返回来执行之前的子程序,但是它的相关信息还是 ...

  4. 带你简单了解python协程和异步

    带你简单了解python的协程和异步 前言 对于学习异步的出发点,是写爬虫.从简单爬虫到学会了使用多线程爬虫之后,在翻看别人的博客文章时偶尔会看到异步这一说法.而对于异步的了解实在困扰了我好久好久,看 ...

  5. 异步等待的 Python 协程

    现在 Python 已经支持用协程进行异步处理.但最近有建议称添加协程以全面完善 Python 的语言结构,而不是像现在这样把他们作为生成器的一个类型.此外,两个新的关键字---异步(async)和等 ...

  6. Day10 - Python协程、异步IO、redis缓存、rabbitMQ队列

    Python之路,Day9 - 异步IO\数据库\队列\缓存   本节内容 Gevent协程 Select\Poll\Epoll异步IO与事件驱动 Python连接Mysql数据库操作 RabbitM ...

  7. day-5 python协程与I/O编程深入浅出

    基于python编程语言环境,重新学习了一遍操作系统IO编程基本知识,同时也学习了什么是协程,通过实际编程,了解进程+协程的优势. 一.python协程编程实现 1.  什么是协程(以下内容来自维基百 ...

  8. Python协程、异步IO

    本节内容 Gevent协程 Select\Poll\Epoll异步IO与事件驱动 Python连接Mysql数据库操作 RabbitMQ队列 Redis\Memcached缓存 Paramiko SS ...

  9. python协程的简单了解

    协程: 协程,又称微线程,纤程.英文名Coroutine. 可以在不同的函数间切换,而且切换的次数和时间都是由用户自己确定的. 协程的几种实现方式: (1)使用生成器yield实现. 如果不了解生成器 ...

随机推荐

  1. Crytek的幕后花絮

    无论是哪种公司规模和状态.Xsolla都能够为其提供定制化的服务.我们提供定制化的技术集成,而不是提供一系列的解决方式.由于我们致力于满足每个合作伙伴的需求.整套的解决方式还存在着一系列的潜在隐患,我 ...

  2. less01

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...

  3. C++学习第一天--编译命令

    前一个月的时间主要是在捯饬自己的ubuntu vim环境,昨天终于都搞好了,从今天开始,学习C++.至于为什么学习C++,其实很大一部分原因还是因为自己喜欢vim,又听说vim对C++的支持还不错,所 ...

  4. bzoj1005: [HNOI2008]明明的烦恼(prufer+高精度)

    1005: [HNOI2008]明明的烦恼 题目:传送门 题解: 毒瘤题啊天~ 其实思考的过程还是比较简单的... 首先当然还是要了解好prufer序列的基本性质啦 那么和1211大体一致,主要还是利 ...

  5. Session原理、安全以及最基本的Express和Redis实现

    Session原理.安全以及最基本的Express和Redis实现 https://segmentfault.com/a/1190000002630691

  6. rest_framework-认证-总结完结篇

    执行过程 APIView() Ruquest() Authentication() OrderView()APIView() def duspatch: self.initial(request) d ...

  7. Auto-Publishing and Monitoring APIs With Spring Boot--转

    原文地址:https://dzone.com/articles/auto-publishing-amp-monitoring-apis-with-spring-bo If you are headin ...

  8. 存储过程的调用和Parameters数组的调用

    记录一些杂记吧 declare @d datetime set @d=GETDATE() select * from [ACC_B2B].[dbo].ORDER_Head --where 1=1 se ...

  9. DefaultView 的作用(对DataSet查询出的来数据进行排序)

    DefaultView 的作用 收藏    一直以来在对数据进行排序, 条件查询都是直接重复构建SQL来进行, 在查询次数和数据量不多的情况下倒没觉得什么, 但慢慢得, 当程序需要对大量数据椐不同条件 ...

  10. ui5 call view or method from another view

    // call view or method from another view //# view call // var view2=sap.ui.jsview("ui5d.popup01 ...