Python协程操作之gevent(yield阻塞,greenlet),协程实现多任务(有规律的交替协作执行)用法详解


Posted in Python onOctober 14, 2019

本文实例讲述了Python 协程操作之gevent(yield阻塞,greenlet),协程实现多任务(有规律的交替协作执行)用法。分享给大家供大家参考,具体如下:

实现多任务:进程消耗的资源最大,线程消耗的资源次之,协程消耗的资源最少(单线程)。

gevent实现协程,gevent是通过阻塞代码(例如网络延迟等)来自动切换要执行的任务,所以在进行IO密集型程序时(例如爬虫),使用gevent可以提高效率(有效利用网络延迟的时间去执行其他任务)。

GIL(全局解释器锁)是C语言版本的Python解释器中专有的,GIL的存在让多线程的效率变低(哪个线程抢到锁,就执行哪个线程)。在IO密集型程序中,多线程依然比单线程效率高(GIL通过IO阻塞自动切换多线程)。

解决GIL(全局解释器锁)的问题的三种方法:1、不要用C语言版本的Python解释器。2、让子线程运行其他语言代码(例如:主线程运行Python代码,子线程运行C语言代码(C语言的动态库))。3、多进程代替多线程(多进程可以利用多核CPU)。

demo.py(协程底层原理,yield):

import time
# 带yield的函数并不是函数,而是一个生成器模板,返回一个生成器(可用于遍历迭代)
def task_1():
  while True:
    time.sleep(0.1)
    yield # 阻塞,等待next迭代解阻塞
def task_2():
  while True:
    time.sleep(0.1)
    yield
def main():
  t1 = task_1() # 返回的t1是一个生成器。 此时并未执行task_1中的代码
  t2 = task_2()
  # 先让t1运行一会,当t1中遇到yield的时候,再返回到24行,然后
  # 执行t2,当它遇到yield的时候,再次切换到t1中
  # 这样t1/t2/t1/t2的交替运行,最终实现了多任务....协程
  while True:
    next(t1) # 执行task_1中的代码,遇到yield阻塞task_1。 等待下一次next激活。
    next(t2) # 实现task_1与task_2交替执行,实现多任务...协程
if __name__ == "__main__":
  main()

demo.py(greenlet实现协程,封装了yield):

from greenlet import greenlet # 需要安装greenlet模块 sudo pip3 install greenlet (python2.x使用pip) 
import time
def test1():
  while True:
    print("---A--")
    gr2.switch()  # 切换到gr2中的任务。
    time.sleep(0.5)
def test2():
  while True:
    print("---B--")
    gr1.switch()  # 切换到gr1中的任务。
    time.sleep(0.5)
gr1 = greenlet(test1) # greenlet 底层封装了yield。
gr2 = greenlet(test2)
#切换到gr1中运行
gr1.switch()

demo.py(gevent实现协程,封装了greenlet,遇到阻塞代码自动切换协程任务):

import gevent # 需要安装gevent模块 sudo pip3 install gevent (python2.x使用pip) 
import time
def f1(n):
  for i in range(n):
    print(gevent.getcurrent(), i)
    gevent.sleep(0.5)  # 为了提高协程效率,遇到阻塞类代码,会自动切换协程任务。
    # time.sleep(0.5)  # 阻塞类代码必须使用gevent自己包装的代码,原生阻塞类代码不会切换协程任务。 
              # 可以使用monkey.patch_all()将所有原生阻塞类代码替换成gevent包装的阻塞类代码。 
def f2(n):
  for i in range(n):
    print(gevent.getcurrent(), i) # <Greenlet "Greenlet-0" at 0x7f4a09b34648: f1(5)> 0
    gevent.sleep(0.5)
    # time.sleep(0.5)
def f3(n):
  for i in range(n):
    print(gevent.getcurrent(), i)
    gevent.sleep(0.5)
    # time.sleep(0.5)
g1 = gevent.spawn(f1, 5) # gevent其实是对greenlet的封装。
g2 = gevent.spawn(f2, 5) # 第一个参数f2表示协程执行的具体任务(函数),第二个参数5表示要传给f2的参数
g3 = gevent.spawn(f3, 5)
g1.join()  # 遇到阻塞类代码,自动切换协程任务。
g2.join()
g3.join()

运行结果:

<Greenlet at 0x1985030: f1(5)> 0
<Greenlet at 0x1b431c8: f2(5)> 0
<Greenlet at 0x1b43140: f3(5)> 0
<Greenlet at 0x1985030: f1(5)> 1
<Greenlet at 0x1b431c8: f2(5)> 1
<Greenlet at 0x1b43140: f3(5)> 1
<Greenlet at 0x1985030: f1(5)> 2
<Greenlet at 0x1b431c8: f2(5)> 2
<Greenlet at 0x1b43140: f3(5)> 2
<Greenlet at 0x1985030: f1(5)> 3
<Greenlet at 0x1b431c8: f2(5)> 3
<Greenlet at 0x1b43140: f3(5)> 3
<Greenlet at 0x1985030: f1(5)> 4
<Greenlet at 0x1b431c8: f2(5)> 4
<Greenlet at 0x1b43140: f3(5)> 4

demo.py(gevent打补丁,monkey自动替换原生阻塞类代码。重要,常用):

import gevent # 需要安装gevent模块 sudo pip3 install gevent (python2.x使用pip)
import time
from gevent import monkey
# gevent打补丁
monkey.patch_all() # 将所有原生阻塞类代码自动替换成gevent包装的阻塞类代码。 
def f1(n):
  for i in range(n):
    print(gevent.getcurrent(), i)
    time.sleep(0.5) # 会自动替换成 gevent.sleep(0.5)
def f2(n):
  for i in range(n):
    print(gevent.getcurrent(), i)
    time.sleep(0.5)
def f3(n):
  for i in range(n):
    print(gevent.getcurrent(), i)
    time.sleep(0.5)
# g1 = gevent.spawn(f1, 5)
# g2 = gevent.spawn(f2, 5)
# g3 = gevent.spawn(f3, 5)
# g1.join()
# g2.join()
# g3.join()
# 一种简便写法
gevent.joinall([
    gevent.spawn(f1, 5),
    gevent.spawn(f2, 5),
    gevent.spawn(f3, 5)
])

运行结果:

<Greenlet at 0x22e0938: f1(5)> 0
<Greenlet at 0x22e09c0: f2(5)> 0
<Greenlet at 0x22e0a48: f3(5)> 0
<Greenlet at 0x22e0938: f1(5)> 1
<Greenlet at 0x22e09c0: f2(5)> 1
<Greenlet at 0x22e0a48: f3(5)> 1
<Greenlet at 0x22e0938: f1(5)> 2
<Greenlet at 0x22e09c0: f2(5)> 2
<Greenlet at 0x22e0a48: f3(5)> 2
<Greenlet at 0x22e0938: f1(5)> 3
<Greenlet at 0x22e09c0: f2(5)> 3
<Greenlet at 0x22e0a48: f3(5)> 3
<Greenlet at 0x22e0938: f1(5)> 4
<Greenlet at 0x22e09c0: f2(5)> 4
<Greenlet at 0x22e0a48: f3(5)> 4

demo.py(gevent底层原理):

import socket
import time
tcp_server_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_server_tcp.bind(("", 7899))
tcp_server_tcp.listen(128)
tcp_server_tcp.setblocking(False) # 设置套接字为非堵塞的方式。 (接收数据时如果没有接到数据(阻塞)那么就抛异常,否则正常接收数据。)
client_socket_list = list() # 用于保存与客户端连接的套接字。
while True:
  # time.sleep(0.5)
  try:
    new_socket, new_addr = tcp_server_tcp.accept() # 用抛异常的方式代替阻塞。
  except Exception as ret:
    print("---没有新的客户端到来---")
  else:
    print("---只要没有产生异常,那么也就意味着 来了一个新的客户端----")
    new_socket.setblocking(False) # 设置套接字为非堵塞的方式。 (如果需要阻塞就直接抛异常代替阻塞)
    client_socket_list.append(new_socket)
  for client_socket in client_socket_list:
    try:
      recv_data = client_socket.recv(1024)  # 用抛异常的方式代替阻塞。
    except Exception as ret:
      print("----这个客户端还没有发送过来数据----")
    else:
      if recv_data:
        # 对方发送过来数据
        print("----客户端发送过来了数据-----")
      else:
        # 对方调用close 导致了 recv返回
        client_socket.close()
        client_socket_list.remove(client_socket)
        print("---客户端已经关闭----")

希望本文所述对大家Python程序设计有所帮助。

Python 相关文章推荐
Python读取环境变量的方法和自定义类分享
Nov 22 Python
Python常用随机数与随机字符串方法实例
Apr 09 Python
Python中模块string.py详解
Mar 12 Python
Python简单的制作图片验证码实例
May 31 Python
梯度下降法介绍及利用Python实现的方法示例
Jul 12 Python
解决python xlrd无法读取excel文件的问题
Dec 25 Python
Python面向对象之类的定义与继承用法示例
Jan 14 Python
如何利用Python分析出微信朋友男女统计图
Jan 25 Python
基于Python安装pyecharts所遇的问题及解决方法
Aug 12 Python
Python基于pyjnius库实现访问java类
Jul 31 Python
Python将字典转换为XML的方法
Aug 01 Python
关于python3.9安装wordcloud出错的问题及解决办法
Nov 02 Python
Python 闭包,函数分隔作用域,nonlocal声明非局部变量操作示例
Oct 14 #Python
win10子系统python开发环境准备及kenlm和nltk的使用教程
Oct 14 #Python
python web框架Flask实现图形验证码及验证码的动态刷新实例
Oct 14 #Python
执行Django数据迁移时报 1091错误及解决方法
Oct 14 #Python
解析Python3中的Import
Oct 13 #Python
Python英文文章词频统计(14份剑桥真题词频统计)
Oct 13 #Python
Python 转换RGB颜色值的示例代码
Oct 13 #Python
You might like
PHP strip_tags() 去字符串中的 HTML、XML 以及 PHP 标签的函数
2016/05/22 PHP
thinkphp5框架实现的自定义扩展类操作示例
2019/05/16 PHP
IE8 chrome中table隔行换色解决办法
2010/07/09 Javascript
JS 面向对象之神奇的prototype
2011/02/26 Javascript
子页向父页传值示例
2013/11/27 Javascript
jQuery实现拖拽效果插件的方法
2015/03/23 Javascript
JS设计模式之策略模式概念与用法分析
2018/02/05 Javascript
node下使用UglifyJS压缩合并JS文件的方法
2018/03/07 Javascript
mock.js实现模拟生成假数据功能示例
2019/01/15 Javascript
JavaScript使用小插件实现倒计时的方法讲解
2019/03/11 Javascript
JS防抖和节流实例解析
2019/09/24 Javascript
vue中实现动态生成二维码的方法
2020/02/21 Javascript
云服务器部署Node.js项目的方法步骤(小白系列)
2020/03/23 Javascript
vue实现简单图片上传
2020/06/30 Javascript
python正则表达式修复网站文章字体不统一的解决方法
2013/02/21 Python
Python入门篇之条件、循环
2014/10/17 Python
使用Python中的cookielib模拟登录网站
2015/04/09 Python
在Python中使用CasperJS获取JS渲染生成的HTML内容的教程
2015/04/09 Python
快速排序的算法思想及Python版快速排序的实现示例
2016/07/02 Python
Python面向对象之Web静态服务器
2019/09/03 Python
wxPython实现分隔窗口
2019/11/19 Python
Python Pickle 实现在同一个文件中序列化多个对象
2019/12/30 Python
Algenist奥杰尼官网:微藻抗衰老护肤品牌
2017/07/15 全球购物
莫斯科制造商的廉价皮大衣:Fursk
2020/06/09 全球购物
JMS中Topic和Queue有什么区别
2013/05/15 面试题
办公室内勤岗位职责范本
2013/12/09 职场文书
市场开发与营销专业求职信
2013/12/31 职场文书
商场拾金不昧表扬信
2014/01/13 职场文书
意外死亡赔偿协议书
2014/10/14 职场文书
2014年社区矫正工作总结
2014/11/18 职场文书
2016自主招生教师推荐信范文
2015/03/23 职场文书
安全生产学习心得体会
2016/01/18 职场文书
2019奶茶店创业计划书范本!
2019/07/15 职场文书
简述Java中throw-throws异常抛出
2021/08/07 Java/Android
CDPR谈《巫师》新作用虚幻5原因 称不会为Epic独占
2022/04/06 其他游戏
python中urllib包的网络请求教程
2022/04/19 Python