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进行测试。

目录
相关文章
|
3月前
|
数据采集 机器学习/深度学习 人工智能
Python:现代编程的首选语言
Python:现代编程的首选语言
304 102
|
3月前
|
数据采集 机器学习/深度学习 算法框架/工具
Python:现代编程的瑞士军刀
Python:现代编程的瑞士军刀
325 104
|
3月前
|
人工智能 自然语言处理 算法框架/工具
Python:现代编程的首选语言
Python:现代编程的首选语言
266 103
|
3月前
|
机器学习/深度学习 人工智能 数据挖掘
Python:现代编程的首选语言
Python:现代编程的首选语言
199 82
|
2月前
|
Python
Python编程:运算符详解
本文全面详解Python各类运算符,涵盖算术、比较、逻辑、赋值、位、身份、成员运算符及优先级规则,结合实例代码与运行结果,助你深入掌握Python运算符的使用方法与应用场景。
190 3
|
2月前
|
数据处理 Python
Python编程:类型转换与输入输出
本教程介绍Python中输入输出与类型转换的基础知识,涵盖input()和print()的使用,int()、float()等类型转换方法,并通过综合示例演示数据处理、错误处理及格式化输出,助你掌握核心编程技能。
457 3
|
2月前
|
并行计算 安全 计算机视觉
Python多进程编程:用multiprocessing突破GIL限制
Python中GIL限制多线程性能,尤其在CPU密集型任务中。`multiprocessing`模块通过创建独立进程,绕过GIL,实现真正的并行计算。它支持进程池、队列、管道、共享内存和同步机制,适用于科学计算、图像处理等场景。相比多线程,多进程更适合利用多核优势,虽有较高内存开销,但能显著提升性能。合理使用进程池与通信机制,可最大化效率。
277 3
|
2月前
|
Java 调度 数据库
Python threading模块:多线程编程的实战指南
本文深入讲解Python多线程编程,涵盖threading模块的核心用法:线程创建、生命周期、同步机制(锁、信号量、条件变量)、线程通信(队列)、守护线程与线程池应用。结合实战案例,如多线程下载器,帮助开发者提升程序并发性能,适用于I/O密集型任务处理。
276 0
|
3月前
|
数据采集 网络协议 API
协程+连接池:高并发Python爬虫的底层优化逻辑
协程+连接池:高并发Python爬虫的底层优化逻辑
|
3月前
|
数据采集 机器学习/深度学习 人工智能
Python:现代编程的多面手
Python:现代编程的多面手
85 0

推荐镜像

更多