Python协程之Gevent模块
背景
进程是操作系统分配资源的最小单位,每个进程独享4G的内存地址空间,因此进程内数据是安全的,检查间的通信需要使用特定的方法。同理,正是因为进程是数据安全的,所以导致进程的切换是一个很麻烦效率不高的操作。为了解决进程切换带来的问题,线程这个名词出现了,一个进程可以包含多个线程,一个进程下的所有线程共享所有的数据,数据可以直接访问,协程的切换比进程的切换更快。进程和线程的切换是有操作系统控制,不是应用程序自己控制,是被动的。为了开发出应用程序自己可控制,百万级任务切换的方案,协程这个概念被提出了。
协程是一个“轻”量级的任务,协程的切换是单纯的应用程序内的代码变动,不涉及操作系统的动作。一个线程可以包含多个协程,协程的并发可以极大的提升代码的运行效率,而且协程的并发不用考虑互斥锁的问题,共享全局数据更简单更安全。
线程适用的场景:
IO密集型操作的系统,协程遇见IO操作自动切换
百万级任务的切换
python和协程
python因为GIL锁的原因,使得python环境下的多线程达不到理想的效果,因为GIL锁保证了同一时间下只有一个线程运行。为了实现python环境下多任务的并发,一般是多进程+多协程。python从3.0版本开始开发自带的协程库asyncio,到3.7版本已经发展成熟到了一定程度,完全可以用于线上运行。在asyncio库出来以前,第三方库Gevent是一个非常优秀的协程库,一直到现在还在被广泛使用。
Gevent库
例子:Gevent实现socket服务端和客户端之间的通信,以及使用queue进行协程见的消息通信。
from gevent import monkey; monkey.patch_all()
import gevent
import queue
import time
from socket import * q = queue.Queue() def func1():
while True:
msg = q.get()
print("func1 : %s" % (msg, )) def func2():
s = socket(AF_INET, SOCK_STREAM)
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, )
s.bind(("127.0.0.1", ))
s.listen()
while True:
conn, addr = s.accept()
while True:
res = conn.recv()
q.put(res)
conn.send("ok".encode('utf-8')) gevent.joinall([
gevent.spawn(func1),
gevent.spawn(func2)
])
from socket import * client = socket(AF_INET, SOCK_STREAM)
client.connect(('127.0.0.1', )) while True:
msg = input('>>: ').strip()
client.send(msg.encode('utf-8'))
msg = client.recv()
print(msg)
注意:
- Gevent实现遇见IO自动切换的功能,需要调用如下代码from gevent import monkey; monkey.patch_all()实现,且处于该代码下面的代码才能自动切换,处于代码前面的代码不能自动切换
- 使用queue时,如果协程的等待IO全部是在从queue获取数据,那么Gevent会报异常“gevent.exceptions.LoopExit: This operation would block forever”,如下例子。解决的办法是不让Gevent全部等待在queue的get操作即可,如上面的socket例子。
from gevent import monkey; monkey.patch_all()
import gevent
import queue
import time
from socket import * q = queue.Queue() def func1():
while True:
msg = q.get()
print("func1 : %s" % (msg, )) def func2():
q.put("abc".encode('utf-8'))
time.sleep()
q.put("def".encode('utf-8')) gevent.joinall([
gevent.spawn(func1),
gevent.spawn(func2)
])
asyncio库(python3.7以上)
import asyncio async def foo():
print('----start foo')
await asyncio.sleep()
print('----end foo') async def bar():
print('****start bar')
await asyncio.sleep()
print('****end bar') async def main():
# res = await asyncio.gather(foo(), bar()) # 所有的协程并发执行
# 等价
task1 = asyncio.create_task(foo())
task2 = asyncio.create_task(bar())
await task1
await task2 if __name__ == '__main__':
asyncio.run(main()) # 结果
----start foo
****start bar
----end foo
****end bar
将上述socker例子,使用asyncio开发,如下所示。
import asyncio async def myprint():
while True:
msg = await q.get()
print("myprint: %s" % msg)
q.task_done() async def handle(reader, writer):
while True:
data = await reader.read()
message = data.decode()
addr = writer.get_extra_info('peername') await q.put(f"Received {message!r} from {addr!r}") writer.write("ok".encode('UTF-8'))
await writer.drain() async def myserver():
server = await asyncio.start_server(handle, '127.0.0.1', )
async with server:
await server.serve_forever() async def main():
global q
q = asyncio.Queue()
await asyncio.gather(myserver(), myprint()) asyncio.run(main())
import asyncio async def tcp_echo_client(message):
reader, writer = await asyncio.open_connection('127.0.0.1', ) while True:
msg = input(">>")
writer.write(msg.encode('UTF-8')) data = await reader.read()
print(f'Received: {data.decode()!r}') asyncio.run(tcp_echo_client('Hello World!'))
Python协程之Gevent模块的更多相关文章
- python编程中的并发------协程gevent模块
任务例子:喝水.吃饭动作需要耗时1S 单任务:(耗时20s) for i in range(10): print('a正在喝水') time.sleep(1) print('a正在吃饭') time. ...
- 协程:gevent模块,遇到i/o自动切换任务 038
协程 : gevent模块,遇到io自动切换任务 from gevent import monkey;monkey.patch_all() # 写在最上面 这样后面的所有阻塞就全部能够识别了 impo ...
- python之协程gevent模块
Gevent官网文档地址:http://www.gevent.org/contents.html 进程.线程.协程区分 我们通常所说的协程Coroutine其实是corporate routine的缩 ...
- python 并发编程 协程 gevent模块
一 gevent模块 gevent应用场景: 单线程下,多个任务,io密集型程序 安装 pip3 install gevent Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步 ...
- 协程--gevent模块(单线程高并发)
先恶补一下知识点,上节回顾 上下文切换:当CPU从执行一个线程切换到执行另外一个线程的时候,它需要先存储当前线程的本地的数据,程序指针等,然后载入另一个线程的本地数据,程序指针等,最后才开始执行.这种 ...
- Python协程之asyncio
asyncio 是 Python 中的异步IO库,用来编写并发协程,适用于IO阻塞且需要大量并发的场景,例如爬虫.文件读写. asyncio 在 Python3.4 被引入,经过几个版本的迭代,特性. ...
- 协程gevent模块和猴子补丁
# pip 装模块 greenlet和gevent # 协程 # 与进程.线程一样也是实现并发的手段 # 创建一个线程.关闭一个线程都需要创建寄存器.栈等.需要消耗时间 # 协程本质上是一个线程 # ...
- python协程之动态添加任务
https://blog.csdn.net/qq_29349715/article/details/79730786 python协程只能运行在事件循环中,但是一旦事件循环运行,又会阻塞当前任务.所以 ...
- python协程--asyncio模块(基础并发测试)
在高并发的场景下,python提供了一个多线程的模块threading,但似乎这个模块并不近人如意,原因在于cpython本身的全局解析锁(GIL)问题,在一段时间片内实际上的执行是单线程的.同时还存 ...
随机推荐
- unknown directive "" in E:\canteen\nginx-1.16.0/conf/nginx.conf:3-------文本编辑器修改nginx配置文件的坑
nignx小白一个,今天在配置nginx的时候,理所当然的用了文本编辑器编辑并保存了一下nginx的nginx.conf配置文件,一不小心就折腾了几个钟. 保存之后就nginx -s reload一下 ...
- Day10-微信小程序实战-交友小程序-自定义callPhone 和copyText组件
---为了方便用户可以拨打电话和复制微信号(下面就要实现这样的两个功能) 注意:在小程序中是没办法直接的添加用户的微信的,所以就只能是复制微信号 (这种东西的话可以直接去做,也可以做成组件,做出组件的 ...
- 「从零单排canal 03」 canal源码分析大纲
在前面两篇中,我们从基本概念理解了canal是一个什么项目,能应用于什么场景,然后通过一个demo体验,有了基本的体感和认识. 从这一篇开始,我们将从源码入手,深入学习canal的实现方式.了解can ...
- SpringCloud教程第4篇:Hystrix(F版本)
在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以相互调用(RPC),在Spring Cloud可以用RestTemplate+Ribbon和Feign来调用.为了保证其高可用,单个服务 ...
- redis基础二----操作hash
上面usr就是hash的名字,usr这个hash中存储了key 为id.name和age的值 一个hash相当于一个数据对象,里面可以存储key为id name age的值 2.批量插入一个hash数 ...
- MongoDB via Dotnet Core数据映射详解
用好数据映射,MongoDB via Dotnet Core开发变会成一件超级快乐的事. 一.前言 MongoDB这几年已经成为NoSQL的头部数据库. 由于MongoDB free schema ...
- php抽奖功能
在项目开发中经常会遇到花钱抽奖类型的需求.但是老板总是担心用户用小钱抽到大奖.这样会导致项目亏损.下边这段代码可以有效制止抽奖项目亏钱. 个人奖池: 语言:thinkphp redis mysql 表 ...
- jQuery动态生成<select>下拉框
前一阵在项目里需要动态生成下拉框,找了一下用jQuery实现比较方便,这里整理一下. 下文所述方法只是本人在项目中遇到问题的解决方法,场景较为简单,也希望能帮助有需要的朋友 1.动态生成下拉框的两种方 ...
- Python并发编程理论篇
Python并发编程理论篇 前言 其实关于Python的并发编程是比较难写的一章,因为涉及到的知识很复杂并且理论偏多,所以在这里我尽量的用一些非常简明的语言来尽可能的将它描述清楚,在学习之前首先要记住 ...
- springboot + rabbitmq 用了消息确认机制,感觉掉坑里了
本文收录在个人博客:www.chengxy-nds.top,技术资源共享,一起进步 最近部门号召大伙多组织一些技术分享会,说是要活跃公司的技术氛围,但早就看穿一切的我知道,这 T M 就是为了刷KPI ...