协程,greenlet原生协程库, gevent库
协程简介
协程(coroutine),又称为微线程,纤程,是一种用户级的轻量级线程。协程拥有自己的寄存器上下文和栈。
协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来时,恢复之前保存的上下文
和栈。因此协程能保存上一次调用的状态,每次协程重入时,相当于进入上一次调用的状态。
在并发编程中,协程与线程类似,每个协程表示一个执行单元,有自己的本地数据,
与其他协程共享全局数据和其他资源。
协程需要用户自己来编写调度逻辑,对于CPU的切换来说,协程是单进程,所以CPU不用
去考虑怎么调度、切换上下文,这就省去了CPU的切换开销,所以协程在一定程度上又好于多线程。
Python通过yield提供了对协程的基本支持,但是不完全,而使用第三方gevent库是更好的选择,
gevent提供了比较完善的协程支持。gevent是一个基于协程的Python网络函数库,使用greenlet在
libev事件循环顶部提供了一个高级别并发性的API。主要特性有以下几点:
- 基于libev的快速事件循环,Linux上是epoll机制。
- 基于greenlet的轻量级执行单位。
- API复用了Python标准库里的内容。
- 支持SSL的协作式sockets。
- 可通过线程池或c-ares实现DNS查询。
目录
yield表达式
在了解协程之前,需要先了解一下生成器中的yield,它不仅可以当做生成器,还能当做一个表达式来使用(yield)
def func():
x = (yield)
print(x)
x = (yield) g = func()
print(next(g)) # 这是第一个yield,就暂停了
g.send('hello world') # 恢复暂停位置,将第一个yield赋值,
# x = hello world,然后又执行到yield,暂停 -->
None
hello world Process finished with exit code 0
需要注意的是: send跟next一样,可以继续暂停的执行,并把send括号里面的东西变成返回值
没有next开始,就不能使用send!
yield协程下的生产者与消费者问题
import random, time def consumer():
while True:
item = (yield) # 接收send的传递内容
print('消费了%s' % item)
time.sleep(1) def producer(consumer): # 主函数
next(consumer) # 开启协程
while True:
item = random.randint(0, 99)
print('生产了%s' % item)
consumer.send(item) # 发送
time.sleep(1) c = consumer() # 生成器对象
producer(c)
greenlet原生协程
greenlet属于三方库,通过 pip install greenlet 安装
什么是greenlet呢?
CPython(标准python)能够通过生成器来实现协程,但使用起来并不方便而python的衍生版Stackless python,实现了原生的协程,便于利用。
于是将stackless中关于协程的代码单独拿出来做成了CPython的扩展包
也就是python环境下的原生协程包
greenlet的价值
一 高性能的远程协程
二 语义更加明确的显示切换
三 直接将函数包装成协程,保持原有代码风格
greenlet下的生产者与消费者问题
from greenlet import greenlet
from time import sleep
from random import randint def consumer():
while True:
item = p.switch() # 切换p,开始暂停,等待恢复(只有恢复的时候才能收到数据)
print('消费了%s' % item)
sleep(1) def producer():
while True:
item = randint(0, 99)
print('生产了%s' % item)
c.switch(item) # 暂停当前协程,并且切换到指定的协程,传参
sleep(1) c = greenlet(consumer) # 直接封装成协程
p = greenlet(producer)
c.switch() # 相当于next的作用,开启协程
gevent协程
什么是gevent?
gevent开始之前可以先了解一下IO多路复用的epoll
gevent通过封装了libev(基于epoll)和greenlet两个库,可以实现类似线程方式的协程
gevent = epoll + greenlet
gevent的价值是什么?
遇到阻塞就切换到另一个协程继续执行
一 使用基于epoll的libev来避开阻塞
二 使用基于gevent的高效协程来切换执行
三 只有阻塞的时候切换,没有轮询的开销,也没有线程的开销
实现方法:
通过 monkey patching功能使得第三方模块变成协作式。
gevent对协程的支持,本质上是 greenlet在实现切换工作。 greenlet工作流程如下:
假如进行访问网络的IO操作时,出现阻塞, greenlet就显式切换到另一段没有被阻塞的代码段执行,
直到原先的阻塞状况消失以后,再自动切换回原来的代码段继续处理。因此, greenlet是一种合理安排的串行方式。
由于IO操作非常耗时,经常使程序处于等待状态,有了 gevent为我们自动切换协程,
就保证总有 greenlet在运行,而不是等待IO,这就是协程一般比多线程效率高的原因。
由于切换是在IO操作时自动完成,所以 gevent需要修改 Python自带的一些标准库,将一些常见的阻塞,
如 socket、 select等地方实现协程跳转,这一过程在启动时通过 monkey patch完成。
gevent实现并发服务器
from gevent import monkey; monkey.patch_socket()
# 猴子补丁,将socket替换成一个封装了epoll的socket
from socket import socket
import gevent server = socket() # 封装好的socket
server.bind(('', 7788))
server.listen(1000) def recv(conn):
while True:
recv_date = conn.recv(1024).decode()
if recv_date:
print(recv_date)
conn.send(recv_date.encode())
else:
conn.close() while True:
conn, addr = server.accept()
# 生成一个协程,并将conn作为参数传入
gevent.spawn(recv, conn)
gevent实现生产者与消费者问题
import gevent
from gevent import monkey; monkey.patch_all()
# all是所有的能切换成协程的地方全部切换,他包含了socket,一般情况下都是用all
from random import randint
from time import sleep
from gevent.queue import Queue # gevent里面的队列 queue = Queue(3) def consumer():
while True:
item = queue.get() # 空就阻塞
print('消费了%s' % item)
sleep(1) def producer():
while True:
item = randint(0, 99)
queue.put(item) # 满就阻塞 epoll阻塞就切换
print('生产了%s' % item)
sleep(1) c = gevent.spawn(consumer) #封装成协程
p = gevent.spawn(producer)
gevent.joinall([c, p]) # 等待传入协程结束
协程,greenlet原生协程库, gevent库的更多相关文章
- yield、greenlet与协程gevent
yield 在说明yield之前,我们了解python中一些概念. 在了解Python的数据结构时,容器(container).可迭代对象(iterable).迭代器(iterator).生成器(ge ...
- Python之路(第四十七篇) 协程:greenlet模块\gevent模块\asyncio模块
一.协程介绍 协程:是单线程下的并发,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的. 协程相比于线程,最大的区别在于 ...
- python --- 协程编程(第三方库gevent的使用)
1. 什么是协程? 协程(coroutine),又称微线程.协程不是线程也不是进程,它的上下文关系切换不是由CPU控制,一个协程由当前任务切换到其他任务由当前任务来控制.一个线程可以包含多个协程,对于 ...
- 基于协程的Python网络库gevent
import gevent def test1(): print 12 gevent.sleep(0) print 34 def test2(): print 56 gevent.sleep(0) p ...
- 协程----greenlet模块,gevent模块
1.协程初识,greenlet模块 2.gevent模块(需要pip安装) 一.协程初识,greenlet模块: 协程:是单线程下的并发,又称微线程,纤程.英文名Coroutine.一句话说明什么是线 ...
- python协程初步--gevent库使用以及解释什么是猴子补丁monkey_patch
协程工作的特点是遇到阻塞或耗时的任务时就切换,协程的生存依赖于线程,线程依赖于进程 一个似乎有点问题的例子 import gevent,time def kisscpc(num): for i in ...
- Python3的原生协程(Async/Await)和Tornado异步非阻塞
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_113 我们知道在程序在执行 IO 密集型任务的时候,程序会因为等待 IO 而阻塞,而协程作为一种用户态的轻量级线程,可以帮我们解决 ...
- 小议Python3的原生协程机制
此文已由作者张耕源授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 在最近发布的 Python 3.5 版本中,官方正式引入了 async/await关键字.在 asyncio ...
- Python 线程----线程方法,线程事件,线程队列,线程池,GIL锁,协程,Greenlet
主要内容: 线程的一些其他方法 线程事件 线程队列 线程池 GIL锁 协程 Greenlet Gevent 一. 线程(threading)的一些其他方法 from threading import ...
随机推荐
- Gym - 101982B 2018-2019 ACM-ICPC Pacific Northwest Regional Contest (Div. 1) B. Coprime Integers Mobius+容斥 ab间gcd(x,y)=1的对数
题面 题意:给你 abcd(1e7),求a<=x<=b,c<=y<=d的,gcd(x,y)=1的数量 题解:经典题目,求从1的到n中选x,从1到m中选y的,gcd(x,y)=k ...
- 页面渐进式消失【JS代码】
// 设定时间,页面慢慢变透明,直到消失 var opacityInterval = setInterval(function () { // 普通时间转为格林威治时间 var targetDate ...
- HTML--使用下拉列表框,节省空间
下拉列表在网页中也常会用到,它可以有效的节省网页空间.既可以单选.又可以多选.如下代码: 讲解: 1.value: 2.selected="selected": 设置selecte ...
- BZOJ 2300凸包+离线
思路: 倒着加显然吧 动态维护这个凸包就好了 //By SiriusRen #include <bits/stdc++.h> using namespace std; ; int n,m ...
- Jquery EasyUI环境下设置下拉框选中指定选项
前提: 要求点击某个按钮就将所有的下拉框都设置为选中第一个选项,因此,指定索引是最好的做法 尝试过的做法: html代码如下(easyui 的写法) <select class="ea ...
- echarts通过ajax请求展示多叉树
背景:在sqlserver使用过程中经常由于各种原因会出现阻塞,并发数较高,很难肉眼看出那个session阻塞了其他process,通过sql查询出根源也需要大量的重复操作才能够找到. 因此就有这方面 ...
- Spring学习_day03_事务
本文为博主辛苦总结,希望自己以后返回来看的时候理解更深刻,也希望可以起到帮助初学者的作用. 转载请注明 出自 : luogg的博客园 谢谢配合! Spring_day03 一.事务 1.1 事务 事务 ...
- 常见Android安装启动失败问题
1.INSTALL_FAILED_VERSION_DOWNGRADE版本过低2.Failed to install Funm_AND.apk on device 'QWOJLVR8KNHYA6NR': ...
- Android之Fragment的优点和作用
一:什么是Fragment 碎片.片段.其目的是为了解决不同屏幕分辩率的动态和灵活UI设计.大屏幕如平板小屏幕如手机,平板电脑的设计使得其有更多的空间来放更多的UI组件,而多出来的空间存放UI使其会产 ...
- centos添加永久的环境变量
cd /etc/profile.d/ 创建一个sh文件 vi dotnetpath.sh 内容如下: export PATH=$PATH:/opt/dotnet 保存,重启,这就有了一个永久的环境变量