7Python全栈之路系列之协程

简介:

What is the association?

与子例程一样,协程也是一种程序组件。 相对子例程而言,协程更为一般和灵活,但在实践中使用没有子例程那样广泛。 协程源自Simula和Modula-2语言,但也有其他语言支持。 协程更适合于用来实现彼此熟悉的程序组件,如合作式多任务,迭代器,无限列表和管道。

来自维基百科 https://zh.wikipedia.org/wiki/协程


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

协程的优缺点:

优点

  1. 无需线程上下文切换的开销

  2. 无需原子操作锁定及同步的开销(更改一个变量)

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

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

缺点:

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

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

实现协程实例

yield

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def  consumer(name):
     print ( "--->starting eating baozi..." )
     while  True :
         new_baozi  =  yield   # 直接返回
         print ( "[%s] is eating baozi %s"  %  (name, new_baozi))
         
def  producer():
     =  con.__next__()
     =  con2.__next__()
     =  0
     while  n <  5 :
         + =  1
         con.send(n)   # 唤醒生成器的同时传入一个参数
         con2.send(n)
         print ( "\033[32;1m[producer]\033[0m is making baozi %s"  %  n)
         
if  __name__  = =  '__main__' :
     con  =  consumer( "c1" )
     con2  =  consumer( "c2" )
     =  producer()

Greenlet

安装greenlet

1
pip3 install greenlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# -*- coding:utf-8 -*-
from  greenlet  import  greenlet
 
def  func1():
     print ( 12 )
     gr2.switch()
     print ( 34 )
     gr2.switch()
     
def  func2():
     print ( 56 )
     gr1.switch()
     print ( 78 )
     
# 创建两个携程
gr1  =  greenlet(func1)
gr2  =  greenlet(func2)
gr1.switch()   # 手动切换

Gevent

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

安装Gevent

1
pip3 install gevent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import  gevent
 
def  foo():
     print ( 'Running in foo' )
     gevent.sleep( 2 )
     print ( 'Explicit context switch to foo again' )
     
def  bar():
     print ( 'Explicit context to bar' )
     gevent.sleep( 3 )
     print ( 'Implicit context switch back to bar' )
     
# 自动切换
gevent.joinall([
     gevent.spawn(foo),   # 启动一个协程
     gevent.spawn(bar),
])

页面抓取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from  urllib  import  request
from  gevent  import  monkey
import  gevent
import  time
 
monkey.patch_all()   # 当前程序中只要设置到IO操作的都做上标记
 
def  wget(url):
     print ( 'GET: %s'  %  url)
     resp  =  request.urlopen(url)
     data  =  resp.read()
     print ( '%d bytes received from %s.'  %  ( len (data), url))
     
urls  =  [
     'https://www.python.org/' ,
     'https://www.python.org/' ,
     'https://github.com/' ,
     'https://blog.ansheng.me/' ,
]
 
# 串行抓取
start_time  =  time.time()
for  in  urls:
     wget(n)
print ( "串行抓取使用时间:" , time.time()  -  start_time)
 
# 并行抓取
ctrip_time  =  time.time()
gevent.joinall([
     gevent.spawn(wget,  'https://www.python.org/' ),
     gevent.spawn(wget,  'https://www.python.org/' ),
     gevent.spawn(wget,  'https://github.com/' ),
     gevent.spawn(wget,  'https://blog.ansheng.me/' ),
])
print ( "并行抓取使用时间:" , time.time()  -  ctrip_time)

输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
C:\Python\Python35\python.exe E: / MyCodeProjects / 协程 / s4.py
GET: https: / / www.python.org /
47424  bytes received  from  https: / / www.python.org / .
GET: https: / / www.python.org /
47424  bytes received  from  https: / / www.python.org / .
GET: https: / / github.com /
25735  bytes received  from  https: / / github.com / .
GET: https: / / blog.ansheng.me /
82693  bytes received  from  https: / / blog.ansheng.me / .
串行抓取使用时间:  15.143015384674072
GET: https: / / www.python.org /
GET: https: / / www.python.org /
GET: https: / / github.com /
GET: https: / / blog.ansheng.me /
25736  bytes received  from  https: / / github.com / .
47424  bytes received  from  https: / / www.python.org / .
82693  bytes received  from  https: / / blog.ansheng.me / .
47424  bytes received  from  https: / / www.python.org / .
并行抓取使用时间:  3.781306266784668
Process finished with exit code  0









本文转自 Edenwy  51CTO博客,原文链接:http://blog.51cto.com/edeny/1924915,如需转载请自行联系原作者
目录
相关文章
|
3天前
|
网络协议 调度 开发者
python中gevent基于协程的并发编程模型详细介绍
`gevent`是Python的第三方库,提供基于协程的并发模型,适用于I/O密集型任务的高效异步编程。其核心是协程调度器,在单线程中轮流执行多个协程,通过非阻塞I/O实现高并发。主要特点包括协程调度、事件循环的I/O模型、同步/异步编程支持及易用性。示例代码展示了一个使用`gevent`实现的异步TCP服务器,当客户端连接时,服务器以协程方式处理请求,实现非阻塞通信。
17 0
|
3天前
|
数据采集 数据库 C++
python并发编程:并发编程中是选择多线程呢?还是多进程呢?还是多协程呢?
python并发编程:并发编程中是选择多线程呢?还是多进程呢?还是多协程呢?
22 0
|
3天前
|
安全 调度 Python
探索Python中的并发编程:协程与多线程的比较
本文将深入探讨Python中的并发编程技术,重点比较协程与多线程的特点和应用场景。通过对协程和多线程的原理解析,以及在实际项目中的应用案例分析,读者将能够更好地理解两种并发编程模型的异同,并在实践中选择合适的方案来提升Python程序的性能和效率。
|
3天前
|
安全 网络安全 数据库
Python 全栈安全(一)(4)
Python 全栈安全(一)
10 0
|
3天前
|
缓存 安全 Go
Python 全栈安全(四)(4)
Python 全栈安全(四)
14 2
|
3天前
|
安全 中间件 数据库
Python 全栈安全(二)(4)
Python 全栈安全(二)
8 0
|
3天前
|
调度 Python
探索Python中的异步编程:从回调到协程
本文将介绍Python中的异步编程技术,从最初的回调函数到现代的协程模型。通过对比传统的同步编程方式和异步编程的优劣势,我们深入探讨了Python中异步编程的实现原理,以及如何利用asyncio库和async/await关键字来构建高效的异步应用程序。最后,我们还将讨论一些异步编程的最佳实践和常见问题的解决方法。
|
3天前
|
Python
Python中的协程:异步编程的利器
Python中的协程:异步编程的利器
17 1
|
3天前
|
缓存 安全 Linux
深入探索Python中的协程
深入探索Python中的协程
|
3天前
|
调度 数据库 Python
探索Python中的异步编程:从回调到协程
本文将探讨Python中异步编程的演变过程,从最初的回调函数到现代的协程机制。我们将深入了解异步编程的原理、优势以及如何使用Python的asyncio库来实现高效的异步程序。通过本文,读者将了解到异步编程的基本概念、常见的应用场景,以及如何利用Python的强大功能来提升程序的性能和可维护性。