python使用协程实现并发操作的方法详解


Posted in Python onDecember 27, 2019

本文实例讲述了python使用协程实现并发操作的方法。分享给大家供大家参考,具体如下:

协程

协程是一种用户态的轻量级线程,又称微线程。

协程拥有自己的寄存器上下文和栈,调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。

优点:

  1. 无需线程上下文切换的开销
  2. 无需原子操作锁定及同步的开销
  3. 方便切换控制流,简化编程模型
  4. 高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。

所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。

原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序是不可以被打乱,或者切割掉只执行部分。视作整体是原子性的核心。

缺点:

  1. 无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
  2. 进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序

使用Gevent

gevent是python的一个并发框架,以微线程greenlet为核心,使用了epoll事件监听机制以及诸多其他优化而变得高效.

  • 简单示例

gevent的sleep可以交出控制权,当我们在受限于网络或IO的函数中使用gevent,这些函数会被协作式的调度, gevent的真正能力会得到发挥。Gevent处理了所有的细节, 来保证你的网络库会在可能的时候,隐式交出greenlet上下文的执行权。

import gevent
def foo():
  print('running in foo')
  gevent.sleep(0)
  print('com back from bar in to foo')
def bar():
  print('running in bar')
  gevent.sleep(0)
  print('com back from foo in to bar')
# 创建线程并行执行程序
gevent.joinall([
  gevent.spawn(foo),
  gevent.spawn(bar),
])

执行结果

running in foo
running in bar
com back from bar in to foo
com back from foo in to bar

  • 同步异步
import random
import gevent
def task(pid):
  gevent.sleep(random.randint(0, 2) * 0.001)
  print('Task %s done' % pid)
def synchronous():
  for i in range(1, 10):
    task(i)
def asynchronous():
  threads = [gevent.spawn(task, i) for i in range(10)]
  gevent.joinall(threads)
print('Synchronous:')
synchronous()
print('Asynchronous:')
asynchronous()

执行输出

Synchronous:
Task 1 done
Task 2 done
Task 3 done
Task 4 done
Task 5 done
Task 6 done
Task 7 done
Task 8 done
Task 9 done
Asynchronous:
Task 1 done
Task 4 done
Task 5 done
Task 9 done
Task 6 done
Task 0 done
Task 2 done
Task 3 done
Task 7 done
Task 8 done

  • 以子类的方法使用协程

可以子类化Greenlet类,重载它的_run方法,类似多线程和多进程模块

import gevent
from gevent import Greenlet
class Test(Greenlet):
  def __init__(self, message, n):
    Greenlet.__init__(self)
    self.message = message
    self.n = n
  def _run(self):
    print(self.message, 'start')
    gevent.sleep(self.n)
    print(self.message, 'end')
tests = [
  Test("hello", 3),
  Test("world", 2),
]
for test in tests:
  test.start() # 启动
for test in tests:
  test.join() # 等待执行结束
  • 使用monkey patch修改系统标准库(自动切换协程)

当一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。

由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。

由于切换是在IO操作时自动完成,所以gevent需要修改Python自带的一些标准库,这一过程在启动时通过monkey patch完成

import gevent
import requests
from gevent import monkey
monkey.patch_socket()
def task(url):
  r = requests.get(url)
  print('%s bytes received from %s' % (len(r.text), url))
gevent.joinall([
  gevent.spawn(task, 'https://www.baidu.com/'),
  gevent.spawn(task, 'https://www.qq.com/'),
  gevent.spawn(task, 'https://www.jd.com/'),
])

执行输出

2443 bytes received from https://www.baidu.com/
108315 bytes received from https://www.jd.com/
231873 bytes received from https://www.qq.com/

可以看出3个网络操作是并发执行的,而且结束顺序不同

参考链接:http://hhkbp2.github.io/gevent-tutorial/

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

Python 相关文章推荐
Python对小数进行除法运算的正确方法示例
Aug 25 Python
python实现超简单端口转发的方法
Mar 13 Python
Python多线程编程(一):threading模块综述
Apr 05 Python
使用C语言扩展Python程序的简单入门指引
Apr 14 Python
python清除字符串里非字母字符的方法
Jul 02 Python
Django实现的自定义访问日志模块示例
Jun 23 Python
Python基于机器学习方法实现的电影推荐系统实例详解
Jun 25 Python
python操作docx写入内容,并控制文本的字体颜色
Feb 13 Python
python实现在内存中读写str和二进制数据代码
Apr 24 Python
Python调用C语言程序方法解析
Jul 07 Python
matplotlib之pyplot模块之标题(title()和suptitle())
Feb 22 Python
一劳永逸彻底解决pip install慢的办法
May 24 Python
Python调用.NET库的方法步骤
Dec 27 #Python
IronPython连接MySQL的方法步骤
Dec 27 #Python
python基于三阶贝塞尔曲线的数据平滑算法
Dec 27 #Python
python3获取文件中url内容并下载代码实例
Dec 27 #Python
用python拟合等角螺线的实现示例
Dec 27 #Python
PyTorch 对应点相乘、矩阵相乘实例
Dec 27 #Python
pytorch中tensor.expand()和tensor.expand_as()函数详解
Dec 27 #Python
You might like
php Calender(日历)代码分享
2014/01/03 PHP
PHP 面向对象程序设计(oop)学习笔记 (五) - PHP 命名空间
2014/06/12 PHP
php实现给一张图片加上水印效果
2016/01/02 PHP
JS 自动安装exe程序
2008/11/30 Javascript
几个常用的JavaScript字符串处理函数 - split()、join()、substring()和indexOf()
2009/06/02 Javascript
浅析jQuery中常用的元素查找方法总结
2013/07/04 Javascript
jQuery 快速结束当前正在执行的动画
2013/11/20 Javascript
jquery dialog open后,服务器端控件失效的快速解决方法
2013/12/19 Javascript
BootStrap 轮播插件(carousel)支持左右手势滑动的方法(三种)
2016/07/07 Javascript
自定义事件解决重复请求BUG的问题
2017/07/11 Javascript
关于JavaScript中的this指向问题总结篇
2017/07/23 Javascript
Vue-router结合transition实现app前进后退动画切换效果的实例
2017/10/11 Javascript
基于Bootstrap下拉框插件bootstrap-select使用方法详解
2018/08/07 Javascript
解决IE11 vue +webpack 项目中数据更新后页面没有刷新的问题
2018/09/25 Javascript
webstorm中配置Eslint的两种方式及差异比较详解
2018/10/19 Javascript
基于JavaScript或jQuery实现网站夜间/高亮模式
2020/05/30 jQuery
vue+vant实现购物车全选和反选功能
2020/11/17 Vue.js
[01:05:12]2014 DOTA2国际邀请赛中国区预选赛 TongFu VS CIS-GAME
2014/05/21 DOTA
Eclipse + Python 的安装与配置流程
2013/03/05 Python
怎样使用Python脚本日志功能
2016/08/14 Python
网站渗透常用Python小脚本查询同ip网站
2017/05/08 Python
浅谈Python的list中的选取范围
2018/11/12 Python
Python中低维数组填充高维数组的实现
2019/12/02 Python
python pip安装包出现:Failed building wheel for xxx错误的解决
2019/12/25 Python
HTML5 本地存储和内容按需加载的思路和方法
2011/04/07 HTML / CSS
Html5 Canvas动画基础碰撞检测的实现
2018/12/06 HTML / CSS
机械专业个人求职自荐信格式
2013/09/21 职场文书
大学生专业个人学习的自我评价
2013/10/26 职场文书
小学爱国卫生月活动总结
2014/06/30 职场文书
超市仓管员岗位职责范本
2014/09/18 职场文书
党员三严三实心得体会
2014/10/13 职场文书
2014年安置帮教工作总结
2014/12/11 职场文书
校本课程教学计划
2015/01/19 职场文书
2015年纪念“卢沟桥事变”78周年活动方案
2015/05/06 职场文书
男人帮观后感
2015/06/18 职场文书
简单且有用的Python数据分析和机器学习代码
2021/07/02 Python