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

目录
相关文章
|
5天前
|
机器学习/深度学习 人工智能 TensorFlow
人工智能浪潮下的自我修养:从Python编程入门到深度学习实践
【10月更文挑战第39天】本文旨在为初学者提供一条清晰的道路,从Python基础语法的掌握到深度学习领域的探索。我们将通过简明扼要的语言和实际代码示例,引导读者逐步构建起对人工智能技术的理解和应用能力。文章不仅涵盖Python编程的基础,还将深入探讨深度学习的核心概念、工具和实战技巧,帮助读者在AI的浪潮中找到自己的位置。
|
5天前
|
机器学习/深度学习 数据挖掘 Python
Python编程入门——从零开始构建你的第一个程序
【10月更文挑战第39天】本文将带你走进Python的世界,通过简单易懂的语言和实际的代码示例,让你快速掌握Python的基础语法。无论你是编程新手还是想学习新语言的老手,这篇文章都能为你提供有价值的信息。我们将从变量、数据类型、控制结构等基本概念入手,逐步过渡到函数、模块等高级特性,最后通过一个综合示例来巩固所学知识。让我们一起开启Python编程之旅吧!
|
5天前
|
存储 Python
Python编程入门:打造你的第一个程序
【10月更文挑战第39天】在数字时代的浪潮中,掌握编程技能如同掌握了一门新时代的语言。本文将引导你步入Python编程的奇妙世界,从零基础出发,一步步构建你的第一个程序。我们将探索编程的基本概念,通过简单示例理解变量、数据类型和控制结构,最终实现一个简单的猜数字游戏。这不仅是一段代码的旅程,更是逻辑思维和问题解决能力的锻炼之旅。准备好了吗?让我们开始吧!
|
7天前
|
设计模式 算法 搜索推荐
Python编程中的设计模式:优雅解决复杂问题的钥匙####
本文将探讨Python编程中几种核心设计模式的应用实例与优势,不涉及具体代码示例,而是聚焦于每种模式背后的设计理念、适用场景及其如何促进代码的可维护性和扩展性。通过理解这些设计模式,开发者可以更加高效地构建软件系统,实现代码复用,提升项目质量。 ####
|
6天前
|
机器学习/深度学习 存储 算法
探索Python编程:从基础到高级应用
【10月更文挑战第38天】本文旨在引导读者从Python的基础知识出发,逐渐深入到高级编程概念。通过简明的语言和实际代码示例,我们将一起探索这门语言的魅力和潜力,理解它如何帮助解决现实问题,并启发我们思考编程在现代社会中的作用和意义。
|
6天前
|
机器学习/深度学习 数据挖掘 开发者
Python编程入门:理解基础语法与编写第一个程序
【10月更文挑战第37天】本文旨在为初学者提供Python编程的初步了解,通过简明的语言和直观的例子,引导读者掌握Python的基础语法,并完成一个简单的程序。我们将从变量、数据类型到控制结构,逐步展开讲解,确保即使是编程新手也能轻松跟上。文章末尾附有完整代码示例,供读者参考和实践。
|
7天前
|
人工智能 数据挖掘 程序员
Python编程入门:从零到英雄
【10月更文挑战第37天】本文将引导你走进Python编程的世界,无论你是初学者还是有一定基础的开发者,都能从中受益。我们将从最基础的语法开始讲解,逐步深入到更复杂的主题,如数据结构、面向对象编程和网络编程等。通过本文的学习,你将能够编写出自己的Python程序,实现各种功能。让我们一起踏上Python编程之旅吧!
|
8天前
|
数据采集 机器学习/深度学习 人工智能
Python编程入门:从基础到实战
【10月更文挑战第36天】本文将带你走进Python的世界,从基础语法出发,逐步深入到实际项目应用。我们将一起探索Python的简洁与强大,通过实例学习如何运用Python解决问题。无论你是编程新手还是希望扩展技能的老手,这篇文章都将为你提供有价值的指导和灵感。让我们一起开启Python编程之旅,用代码书写想法,创造可能。
|
9天前
|
设计模式 程序员 数据处理
编程之旅:探索Python中的装饰器
【10月更文挑战第34天】在编程的海洋中,Python这艘航船以其简洁优雅著称。其中,装饰器作为一项高级特性,如同船上的风帆,让代码更加灵活和强大。本文将带你领略装饰器的奥秘,从基础概念到实际应用,一起感受编程之美。
|
8天前
|
分布式计算 并行计算 大数据
Python编程中的高效数据处理技巧
Python编程中的高效数据处理技巧
26 0