python --- 协程编程(第三方库gevent的使用)

简介: 1. 什么是协程?  协程(coroutine),又称微线程。协程不是线程也不是进程,它的上下文关系切换不是由CPU控制,一个协程由当前任务切换到其他任务由当前任务来控制。一个线程可以包含多个协程,对于CPU而言,不存在协程这个概念,它是一种轻量级用户态线程(即只针对用户而言)。

1. 什么是协程?

  协程(coroutine),又称微线程。协程不是线程也不是进程,它的上下文关系切换不是由CPU控制,一个协程由当前任务切换到其他任务由当前任务来控制。一个线程可以包含多个协程,对于CPU而言,不存在协程这个概念,它是一种轻量级用户态线程(即只针对用户而言)。协程拥有自己的寄存器上下文和栈,协程调度切换到其他协程时,将寄存器上下文和栈保存,在切回到当前协程的时候,恢复先前保存的寄存器上下文和栈。

2. 在编程中为什么要使用协程?

  使用协程的好处:(1)CPU无需负担上下文的开销;(2)不需加锁(多个线程操作数据时得加锁);(3)由程序员切换控制流,方便编程;(4)高并发、高扩展、低成本(一个CPU支持上万的协程都不是问题)。

   当然,任何事物有优点必有缺点。协程得缺点:(1)协程自己无法利用CPU多核资源(除非与多进程或者多线程配合);(2)遇到阻塞操作会使整个程序阻塞。

 

例一(使用yield实现在任务间的切换):

 1 import time
 2 
 3 def func1(name):
 4     print("----func1 start...----")
 5     for i in range(6):
 6         temp = yield      #每次遇到yield,func1在此处阻塞,直到temp接收到func2中con.send()传来的值
 7         print("%s in the func1" % (str(temp)))
 8         time.sleep(1)
 9 
10 
11 def func2():
12     print("----func2 start...----")
13     con.__next__()     #此处开始真正的func1的调用
14     for i in range(5):
15         con.send(i+1)
16         print("%s in the func2" % i)
17 
18 
19 if __name__ == '__main__':
20     con = func1(1)     #在有yield的函数中此处不是真正的函数调用,打印con便可知道
21     # print(con)
22     p = func2()
使用yield进行任务切换

  注:例一严格来说不能算是协程,只是实现了两个任务之间的切换。

 

3. 既然例一不能算多协程,难么在python中应该如何使用协程?

  greenlet是一个用C实现的协程模块,相比与python自带的yield,它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator(例一中的con=func1(1)就是做这个操作)。

 

例二:

 1 import greenlet
 2 
 3 def func1():
 4     for i in range(1,6):
 5         print(i)
 6         g2.switch()   #切换到g2
 7 
 8 def func2():
 9     words = ['a', 'b', 'c', 'd', 'e']
10     for w in words:
11         print(w)
12         g1.switch()    #切换到g1
13 
14 g1 = greenlet.greenlet(func1)
15 g2 = greenlet.greenlet(func2)
16 g1.switch()   #切换到g1
使用greenlent模块实现任务切换

  注:使用greenlent可以很简单的进行多任务之间的切换,但是程序运行最耗时的便是I/O操作,要使用协程实现高并发,应当是一旦遇到I/O操作就切换到其他任务,等I/O操作完成后在切回到当前任务(这个过程应当是自动的)。

 

4. 那么在python中,如何让任务遇到I/O操作就切换?

  我们使用第三方库gevent来实现。

  gevent的官方定义:gevent is a coroutine -based Python networking library that uses greenlet to provide a high-level synchronous API on top of the libev event loop.

 

例三(gevent的简单使用):

 1 import gevent
 2 import time
 3 
 4 def func1():
 5     print("func1: start.....")
 6     # Put the current greenlet to sleep for at least *seconds*.(模拟I/O操作,任务在此处自动切换)
 7     gevent.sleep(3)
 8     print("func1: end")
 9 
10 def func2():
11     print("func2: start.....")
12     gevent.sleep(0.5)
13     print("func2: end")
14 
15 start_time = time.time()
16 # joinall(greenlets, timeout=None, raise_error=False, count=None)
17 # Wait for the ``greenlets`` to finish.
18 # :return: A sequence of the greenlets that finished before the timeout (if any)expired.
19 gevent.joinall([gevent.spawn(func1),
20                 gevent.spawn(func2)])
21 # spawn(cls, *args, **kwargs)
22 # Create a new :class:`Greenlet` object and schedule it to run ``function(*args, **kwargs)``.
23 # This can be used as ``gevent.spawn`` or ``Greenlet.spawn``.
24 
25 print("cost:", time.time()-start_time)
26 # 通过计算程序运行的时间可以发现程序确实是以单线程达模拟出了多任务并行的操作。
gevent的简单使用

 

例四(gevent和urllib配合同时下载多个网页):

 1 import urllib.request
 2 import gevent,time
 3 import gevent.monkey
 4 
 5 def func(url="", filename=""):
 6     print("Download:%s" % url)
 7     result = urllib.request.urlopen(url)       #请求打开一个网页
 8     data = result.read()     #读取内容
 9     with open(filename, 'wb') as fp:    #写入文档
10         fp.write(data)
11     print("Finish:%s" % url)
12 
13 if __name__ == "__main__":
14     # Do all of the default monkey patching (calls every other applicablefunction in this module).
15     # 相当与做一个标记,做完此操作gevent就可以检测到此程序中所有的I/O操作
16     gevent.monkey.patch_all()
17 
18     async_time = time.time()
19     gevent.joinall([
20         gevent.spawn(func, "http://www.cnblogs.com/God-Li/p/7774497.html", "7774497.html"),
21         gevent.spawn(func, "http://www.gevent.org/", "gevent.html"),
22         gevent.spawn(func, "https://www.python.org/", "python.html"),
23     ])
24     print("async download cost:", time.time()-async_time)
25 
26     start_time = time.time()
27     func("http://www.cnblogs.com/God-Li/p/7774497.html", "7774497.html")
28     func("http://www.gevent.org/", "gevent.html")
29     func("https://www.python.org/", "python.html")
30     print("download cost:", time.time()-start_time)
gevent和urllib配合同时下载多个网页

  注:对上例代码稍加改造,加上对html源码的解析功能,就可以实现一个简单的多并发爬虫。

 

python --- 网络编程Socket中例二的socket_server2使用gevent改造就可以使其成为一个大并发的socket server。

例五(使用gevent实现并发的socket server):

 1 #服务端
 2 import socket
 3 import gevent
 4 import gevent.monkey
 5 
 6 gevent.monkey.patch_all()
 7 
 8 def request_handler(conn):
 9 
10     '''
11     Wait for an incoming connection.  Return a new socket
12     representing the connection, and the address of the client.
13     '''
14     while True:
15         # print("ok")
16         data = conn.recv(1024)         #接收信息,写明要接收信息的最大容量,单位为字节
17         print("server recv:", data)
18         conn.send(data.upper())       #对收到的信息处理,返回到客户端
19 
20 
21 
22 if __name__ == "__main__":
23     address = ("localhost", 6666)  # 写明服务端要监听的地址,和端口号
24     server = socket.socket()  # 生成一个socket对象
25     server.bind(address)  # 用socket对象绑定要监听的地址和端口
26     server.listen()  # 开始监听
27 
28     while True:
29         conn, addr = server.accept()  # 等带新连接接入服务端,返回一个新的socket对象和地址,地址格式同前面格式
30         gevent.spawn(request_handler, conn)
31 
32     server.close()  # 关闭服务端
socket_server2的并发实现

   注:可使用python --- 网络编程Socket中例二的socket_client2进行测试。

目录
相关文章
|
24天前
|
Go 调度 Python
Golang协程和Python协程用法上的那些“不一样”
本文对比了 Python 和 Go 语言中协程的区别,重点分析了调度机制和执行方式的不同。Go 的协程(goroutine)由运行时自动调度,启动后立即执行;而 Python 协程需通过 await 显式调度,依赖事件循环。文中通过代码示例展示了两种协程的实际运行效果。
|
25天前
|
传感器 数据采集 监控
Python生成器与迭代器:从内存优化到协程调度的深度实践
简介:本文深入解析Python迭代器与生成器的原理及应用,涵盖内存优化技巧、底层协议实现、生成器通信机制及异步编程场景。通过实例讲解如何高效处理大文件、构建数据流水线,并对比不同迭代方式的性能特点,助你编写低内存、高效率的Python代码。
100 0
|
3月前
|
Python
Python编程基石:整型、浮点、字符串与布尔值完全解读
本文介绍了Python中的四种基本数据类型:整型(int)、浮点型(float)、字符串(str)和布尔型(bool)。整型表示无大小限制的整数,支持各类运算;浮点型遵循IEEE 754标准,需注意精度问题;字符串是不可变序列,支持多种操作与方法;布尔型仅有True和False两个值,可与其他类型转换。掌握这些类型及其转换规则是Python编程的基础。
210 33
|
2月前
|
数据采集 监控 调度
干货分享“用 多线程 爬取数据”:单线程 + 协程的效率反超 3 倍,这才是 Python 异步的正确打开方式
在 Python 爬虫中,多线程因 GIL 和切换开销效率低下,而协程通过用户态调度实现高并发,大幅提升爬取效率。本文详解协程原理、实战对比多线程性能,并提供最佳实践,助你掌握异步爬虫核心技术。
|
2月前
|
数据采集 分布式计算 大数据
不会Python,还敢说搞大数据?一文带你入门大数据编程的“硬核”真相
不会Python,还敢说搞大数据?一文带你入门大数据编程的“硬核”真相
82 1
|
3月前
|
设计模式 安全 Python
Python编程精进:正则表达式
正则表达式是一种强大的文本处理工具,用于搜索、匹配和提取模式。本文介绍了正则表达式的语法基础,如`\d`、`\w`等符号,并通过实例展示其在匹配电子邮件、验证电话号码、处理日期格式等场景中的应用。同时,文章提醒用户注意性能、编码、安全性等问题,避免常见错误,如特殊字符转义不当、量词使用错误等。掌握正则表达式能显著提升文本处理效率,但需结合实际需求谨慎设计模式。
135 2
|
5月前
|
程序员 测试技术 开发工具
怎么开发Python第三方库?手把手教你参与开源项目!
大家好,我是程序员晚枫。本文将分享如何开发Python第三方库,并以我维护的开源项目 **popdf** 为例,指导参与开源贡献。Popdf是一个PDF操作库,支持PDF转Word、转图片、合并与加密等功能。文章涵盖从fork项目、本地开发、单元测试到提交PR的全流程,适合想了解开源贡献的开发者。欢迎访问[popdf](https://gitcode.com/python4office/popdf),一起交流学习!
195 21
怎么开发Python第三方库?手把手教你参与开源项目!
|
4月前
|
数据采集 安全 BI
用Python编程基础提升工作效率
一、文件处理整明白了,少加两小时班 (敲暖气管子)领导让整理100个Excel表?手都干抽筋儿了?Python就跟铲雪车似的,哗哗给你整利索!
114 11
|
6月前
|
人工智能 Java 数据安全/隐私保护
[oeasy]python081_ai编程最佳实践_ai辅助编程_提出要求_解决问题
本文介绍了如何利用AI辅助编程解决实际问题,以猫屎咖啡的购买为例,逐步实现将购买斤数换算成人民币金额的功能。文章强调了与AI协作时的三个要点:1) 去除无关信息,聚焦目标;2) 将复杂任务拆解为小步骤,逐步完成;3) 巩固已有成果后再推进。最终代码实现了输入验证、单位转换和价格计算,并保留两位小数。总结指出,在AI时代,人类负责明确目标、拆分任务和确认结果,AI则负责生成代码、解释含义和提供优化建议,编程不会被取代,而是会更广泛地融入各领域。
185 28
|
6月前
|
Python
[oeasy]python074_ai辅助编程_水果程序_fruits_apple_banana_加法_python之禅
本文回顾了从模块导入变量和函数的方法,并通过一个求和程序实例,讲解了Python中输入处理、类型转换及异常处理的应用。重点分析了“明了胜于晦涩”(Explicit is better than implicit)的Python之禅理念,强调代码应清晰明确。最后总结了加法运算程序的实现过程,并预告后续内容将深入探讨变量类型的隐式与显式问题。附有相关资源链接供进一步学习。
86 4

热门文章

最新文章

推荐镜像

更多