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 相关文章推荐
Eclipse + Python 的安装与配置流程
Mar 05 Python
Python数据结构之Array用法实例
Oct 09 Python
Python实现竖排打印传单手机号码易撕条
Mar 16 Python
在Python中用keys()方法返回字典键的教程
May 21 Python
PYTHON 中使用 GLOBAL引发的一系列问题
Oct 12 Python
Django unittest 设置跳过某些case的方法
Dec 26 Python
python引用(import)某个模块提示没找到对应模块的解决方法
Jan 19 Python
详解python中@的用法
Mar 27 Python
使用python实现对元素的长截图功能
Nov 14 Python
tensorflow的ckpt及pb模型持久化方式及转化详解
Feb 12 Python
Python2.6版本pip安装步骤解析
Aug 17 Python
详解Python魔法方法之描述符类
May 26 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代码
2012/07/14 PHP
php中define用法实例
2015/07/30 PHP
PHP怎样用正则抓取页面中的网址
2016/08/09 PHP
php获取今日开始时间和结束时间的方法
2017/02/27 PHP
php基于 swoole 实现的异步处理任务功能示例
2019/08/13 PHP
JS父页面与子页面相互传值方法
2014/03/05 Javascript
jQuery制作可自定义大小的拼图游戏
2015/03/30 Javascript
JavaScript获得页面base标签中url的方法
2015/04/03 Javascript
JS实现带关闭功能的阿里妈妈网站顶部滑出banner工具条代码
2015/09/17 Javascript
jQuery使用$.ajax提交表单完整实例
2015/12/11 Javascript
JavaScript省市区三级联动菜单效果
2016/09/21 Javascript
Bootstrap 模态框(Modal)插件代码解析
2016/12/21 Javascript
NodeJs下的测试框架Mocha的简单介绍
2017/02/22 NodeJs
jQuery基于事件控制实现点击显示内容下拉效果
2017/03/07 Javascript
jQuery实现菜单的显示和隐藏功能示例
2018/07/24 jQuery
node中的密码安全(加密)
2018/09/17 Javascript
D3.js(v3)+react 实现带坐标与比例尺的散点图 (V3版本)
2019/05/09 Javascript
简单了解JavaScript异步
2019/05/23 Javascript
vue实现手机端省市区区域选择
2019/09/27 Javascript
分享Angular http interceptors 拦截器使用(推荐)
2019/11/10 Javascript
nodejs实现百度舆情接口应用示例
2020/02/07 NodeJs
JavaScript canvas实现雨滴特效
2021/01/10 Javascript
[06:07]DOTA2-DPC中国联赛 正赛 Ehome vs VG 选手采访
2021/03/11 DOTA
Python运行的17个时新手常见错误小结
2012/08/07 Python
不到40行代码用Python实现一个简单的推荐系统
2019/05/10 Python
django admin组件使用方法详解
2019/07/19 Python
Python利用全连接神经网络求解MNIST问题详解
2020/01/14 Python
ansible动态Inventory主机清单配置遇到的坑
2020/01/19 Python
英国和爱尔兰的自炊式豪华度假小屋:Rural Retreats
2018/06/08 全球购物
台湾良兴购物网:EcLife
2019/12/01 全球购物
Eclipse面试题
2014/03/22 面试题
北京SQL新华信咨询
2016/09/30 面试题
环保倡议书格式范文
2014/05/14 职场文书
高中美术教师事迹材料
2014/08/22 职场文书
2014工程部年度工作总结
2014/12/17 职场文书
Python 如何实现文件自动去重
2021/06/02 Python