python --- 协程编程(第三方库gevent的使用)
1. 什么是协程?
协程(coroutine),又称微线程。协程不是线程也不是进程,它的上下文关系切换不是由CPU控制,一个协程由当前任务切换到其他任务由当前任务来控制。一个线程可以包含多个协程,对于CPU而言,不存在协程这个概念,它是一种轻量级用户态线程(即只针对用户而言)。协程拥有自己的寄存器上下文和栈,协程调度切换到其他协程时,将寄存器上下文和栈保存,在切回到当前协程的时候,恢复先前保存的寄存器上下文和栈。
2. 在编程中为什么要使用协程?
使用协程的好处:(1)CPU无需负担上下文的开销;(2)不需加锁(多个线程操作数据时得加锁);(3)由程序员切换控制流,方便编程;(4)高并发、高扩展、低成本(一个CPU支持上万的协程都不是问题)。
当然,任何事物有优点必有缺点。协程得缺点:(1)协程自己无法利用CPU多核资源(除非与多进程或者多线程配合);(2)遇到阻塞操作会使整个程序阻塞。
例一(使用yield实现在任务间的切换):
import time def func1(name):
print("----func1 start...----")
for i in range(6):
temp = yield #每次遇到yield,func1在此处阻塞,直到temp接收到func2中con.send()传来的值
print("%s in the func1" % (str(temp)))
time.sleep(1) def func2():
print("----func2 start...----")
con.__next__() #此处开始真正的func1的调用
for i in range(5):
con.send(i+1)
print("%s in the func2" % i) if __name__ == '__main__':
con = func1(1) #在有yield的函数中此处不是真正的函数调用,打印con便可知道
# print(con)
p = func2()
使用yield进行任务切换
注:例一严格来说不能算是协程,只是实现了两个任务之间的切换。
3. 既然例一不能算多协程,难么在python中应该如何使用协程?
greenlet是一个用C实现的协程模块,相比与python自带的yield,它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator(例一中的con=func1(1)就是做这个操作)。
例二:
import greenlet def func1():
for i in range(1,6):
print(i)
g2.switch() #切换到g2 def func2():
words = ['a', 'b', 'c', 'd', 'e']
for w in words:
print(w)
g1.switch() #切换到g1 g1 = greenlet.greenlet(func1)
g2 = greenlet.greenlet(func2)
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的简单使用):
import gevent
import time def func1():
print("func1: start.....")
# Put the current greenlet to sleep for at least *seconds*.(模拟I/O操作,任务在此处自动切换)
gevent.sleep(3)
print("func1: end") def func2():
print("func2: start.....")
gevent.sleep(0.5)
print("func2: end") start_time = time.time()
# joinall(greenlets, timeout=None, raise_error=False, count=None)
# Wait for the ``greenlets`` to finish.
# :return: A sequence of the greenlets that finished before the timeout (if any)expired.
gevent.joinall([gevent.spawn(func1),
gevent.spawn(func2)])
# spawn(cls, *args, **kwargs)
# Create a new :class:`Greenlet` object and schedule it to run ``function(*args, **kwargs)``.
# This can be used as ``gevent.spawn`` or ``Greenlet.spawn``. print("cost:", time.time()-start_time)
# 通过计算程序运行的时间可以发现程序确实是以单线程达模拟出了多任务并行的操作。
gevent的简单使用
例四(gevent和urllib配合同时下载多个网页):
import urllib.request
import gevent,time
import gevent.monkey def func(url="", filename=""):
print("Download:%s" % url)
result = urllib.request.urlopen(url) #请求打开一个网页
data = result.read() #读取内容
with open(filename, 'wb') as fp: #写入文档
fp.write(data)
print("Finish:%s" % url) if __name__ == "__main__":
# Do all of the default monkey patching (calls every other applicablefunction in this module).
# 相当与做一个标记,做完此操作gevent就可以检测到此程序中所有的I/O操作
gevent.monkey.patch_all() async_time = time.time()
gevent.joinall([
gevent.spawn(func, "http://www.cnblogs.com/God-Li/p/7774497.html", "7774497.html"),
gevent.spawn(func, "http://www.gevent.org/", "gevent.html"),
gevent.spawn(func, "https://www.python.org/", "python.html"),
])
print("async download cost:", time.time()-async_time) start_time = time.time()
func("http://www.cnblogs.com/God-Li/p/7774497.html", "7774497.html")
func("http://www.gevent.org/", "gevent.html")
func("https://www.python.org/", "python.html")
print("download cost:", time.time()-start_time)
gevent和urllib配合同时下载多个网页
注:对上例代码稍加改造,加上对html源码的解析功能,就可以实现一个简单的多并发爬虫。
对python --- 网络编程Socket中例二的socket_server2使用gevent改造就可以使其成为一个大并发的socket server。
例五(使用gevent实现并发的socket server):
#服务端
import socket
import gevent
import gevent.monkey gevent.monkey.patch_all() def request_handler(conn): '''
Wait for an incoming connection. Return a new socket
representing the connection, and the address of the client.
'''
while True:
# print("ok")
data = conn.recv(1024) #接收信息,写明要接收信息的最大容量,单位为字节
print("server recv:", data)
conn.send(data.upper()) #对收到的信息处理,返回到客户端 if __name__ == "__main__":
address = ("localhost", 6666) # 写明服务端要监听的地址,和端口号
server = socket.socket() # 生成一个socket对象
server.bind(address) # 用socket对象绑定要监听的地址和端口
server.listen() # 开始监听 while True:
conn, addr = server.accept() # 等带新连接接入服务端,返回一个新的socket对象和地址,地址格式同前面格式
gevent.spawn(request_handler, conn) server.close() # 关闭服务端
socket_server2的并发实现
注:可使用python --- 网络编程Socket中例二的socket_client2进行测试。
python --- 协程编程(第三方库gevent的使用)的更多相关文章
- python协程详解,gevent asyncio
python协程详解,gevent asyncio 新建模板小书匠 #协程的概念 #模块操作协程 # gevent 扩展模块 # asyncio 内置模块 # 基础的语法 1.生成器实现切换 [1] ...
- python基于协程的网络库gevent、eventlet
python网络库也有了基于协程的实现,比较著名的是 gevent.eventlet 它两之间的关系可以参照 Comparing gevent to eventlet, 本文主要简单介绍一下event ...
- day-5 python协程与I/O编程深入浅出
基于python编程语言环境,重新学习了一遍操作系统IO编程基本知识,同时也学习了什么是协程,通过实际编程,了解进程+协程的优势. 一.python协程编程实现 1. 什么是协程(以下内容来自维基百 ...
- python中的协程:greenlet和gevent
python中的协程:greenlet和gevent 协程是一中多任务实现方式,它不需要多个进程或线程就可以实现多任务. 1.通过yield实现协程: 代码: import time def A(): ...
- Python协程(真才实学,想学的进来)
真正有知识的人的成长过程,就像麦穗的成长过程:麦穗空的时候,麦子长得很快,麦穗骄傲地高高昂起,但是,麦穗成熟饱满时,它们开始谦虚,垂下麦芒. --蒙田<蒙田随笔全集> *** 上篇论述了关 ...
- Python协程与Go协程的区别二
写在前面 世界是复杂的,每一种思想都是为了解决某些现实问题而简化成的模型,想解决就得先面对,面对就需要选择角度,角度决定了模型的质量, 喜欢此UP主汤质看本质的哲学科普,其中简洁又不失细节的介绍了人类 ...
- python 协程与go协程的区别
进程.线程和协程 进程的定义: 进程,是计算机中已运行程序的实体.程序本身只是指令.数据及其组织形式的描述,进程才是程序的真正运行实例. 线程的定义: 操作系统能够进行运算调度的最小单位.它被包含在进 ...
- Python协程与JavaScript协程的对比
前言 以前没怎么接触前端对JavaScript 的异步操作不了解,现在有了点了解一查,发现 python 和 JavaScript 的协程发展史简直就是一毛一样! 这里大致做下横向对比和总结,便于对这 ...
- [转载] Python协程从零开始到放弃
Python协程从零开始到放弃 Web安全 作者:美丽联合安全MLSRC 2017-10-09 3,973 Author: lightless@Meili-inc Date: 2017100 ...
随机推荐
- Jquery.Uploadify实现批量上传显示进度条 取消 上传后缩略图显示 可删除
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="UpLoad.aspx.cs&q ...
- JavaWeb之response响应中文乱码问题
response向页面响应中文乱码问题 字节流 * 有可能乱码,与中文转换成字节数组.浏览器打开的默认字符编码有关 * 解决方式:将中文转成字节数组的时候和浏览器默认打开的时候采用的字符集一致 re ...
- 在Visual Studio 2013 中使用C++单元测试
本文主要介绍在Visual Studio 2013中对代码进行单元测试的方法,包含了两方面的内容:对已有的Dll文件进行单元测试,以及对已有的源文件进行单元测试. 1. VS2013对DLL文件的单元 ...
- IDEA Maven 三层架构 2、运行 springMVC
运行 SpringMVC 首先要理解 SpringMVC 应用程序的入口是配置文件 web.xml,其路径为"src/main/webapp/WEB-INF/web.xml",通过 ...
- Arrays.asList () 不可添加或删除元素的原因
Java中奖数组转换为List<T>容器有一个很方便的方法 Arrays.asList(T ... a),我通过此方法给容器进行了赋值操作,接着对其进行 添加元素,却发现会抛出一个(jav ...
- 如何开发webpack loader
关于webpack 作为近段时间风头正盛的打包工具,webpack基本占领了前端圈.相信你都不好意思说不知道webpack. 有兴趣的同学可以参考下我很早之前的webpack简介 . 确实webpac ...
- hibernate5使用注解遇到的问题
问题描述 出现MappingException:Unknown entity,看到这个我以为在cfg配置文件中没有配置,实际上我是配置了的,那么问题出在那里呢,既然找不到实体,那么会不会是注解类出现了 ...
- MVVM模式下 DataTemplate 中控件的绑定
今天给ListBox中通过DataTemplate生成的Button绑定命令时,一开始Button始终找不到绑定的命令.现找到了正确的绑定方式,特来记录一下. 先上个正确的示例: <ListBo ...
- ArcGIS二次开发AO软件安装破解教程
最近在做ArcGIS二次开发时,采用C#中的WPF技术,在调研中发现ArcGIS 10.3及以上版本支持WPF技术,但是关于ArcGIS10.3的破解教程甚少,自己尝试了不少方法都失败了,淘@宝@商家 ...
- jdk8 JAVA_OPTS
JAVA_OPTS="-server -Xms1024m -Xmx1024m -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=512m -Djava. ...