原创 2018-01-10 大邓 大邓带你玩python

gevent库中使用的最核心的是Greenlet-一种用C写的轻量级python模块。在任意时间,系统只能允许一个Greenlet处于运行状态。那怎么让程序高并发,从而实现程序高效运行呢?

这就是我们常说的异步,在网络请求中,可以用下面的图清晰的看出异步的效率

串行和异步

高并发的核心是让一个大的任务分成一批子任务,并且子任务会被被系统高效率的调度,实现同步或者异步。在两个子任务之间切换,也就是经常说到的上下文切换。

同步就是让子任务串行,而异步有点影分身之术,但在任意时间点,真身只有一个,子任务并不是真正的并行,而是充分利用了碎片化的时间,让程序不要浪费在等待上。这就是异步,效率杠杆的。

gevent中的上下文切换是通过yield实现。在这个例子中,我们会有两个子任务,互相利用对方等待的时间做自己的事情。这里我们使用gevent.sleep(0)代表程序会在这里停0秒。

import gevent

def foo():
   print('Running in foo')
   gevent.sleep(0)
   print('Explicit context switch to foo again') def bar():
   print('Explicit context to bar')
   gevent.sleep(0)
   print('Implicit context switch back to bar') gevent.joinall([
   gevent.spawn(foo),
   gevent.spawn(bar),])

我谷歌了一下,spawn的意思是分支,这就很好的跟上面的那个图对应起来,加强记忆。spawn-影分身之术。O(∩_∩)O~,让待运行的任务切分成更小的一批子任务。

下面我们看看运行的顺序:

Running in foo
Explicit context to bar
Explicit context switch to foo again
Implicit context switch back to bar

这里我放一个动图,看看整个大的任务的调度顺序

同步异步的顺序问题

同步运行就是串行,123456...,但是异步的顺序是随机的任意的(根据子任务消耗的时间而定)。

下面我们来看个代码

import gevent
import random def task(pid):
   """
   Some non-deterministic task
   """
   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 5 done
Task 6 done
Task 2 done
Task 4 done
Task 7 done
Task 8 done
Task 9 done
Task 0 done
Task 3 done

同步案例中所有的任务都是按照顺序执行,这导致主程序是阻塞式的(阻塞会暂停主程序的执行)。

gevent.spawn会对传入的任务(子任务集合)进行进行调度,gevent.joinall方法会阻塞当前程序,除非所有的greenlet都执行完毕,程序才会结束。

实战

gevent之前写过一期,但只是比较效率。这一期我们要实现gevent到底怎么用,怎么把异步访问得到的数据提取出来。

最近做了个英语文本数据处理的任务,先做词频统计,然后对每个词语标注音标和注释。其中标注音标和注释,我没有词典,只能用爬虫的方式访问有道词典,获取想要的数据。

但是常规的for循环,word by word很慢,于是就想到用gevent。

分析url规律

首先抓包分析,打开开发者工具,清空访问记录。在有道词典搜索框输入“hello”按回车。观察数据请求情况 发现有道的url构建很简单。

#url构建只需要传入word即可
url = "http://dict.youdao.com/w/eng/{}/".format(word)

解析网页数据

def fetch_word_info(word):

    url = "http://dict.youdao.com/w/eng/{}/".format(word)
   
   resp = requests.get(url,headers=headers)
   doc = pq(resp.text)
   
   pros = ''
   for pro in doc.items('.baav .pronounce'):
       pros+=pro.text()
   
   
   description = ''
   for li in doc.items('#phrsListTab .trans-container ul li'):
       description +=li.text()
       
   return {'word':word,'音标':pros,'注释':description}
   

同步代码

因为requests库在任何时候只允许有一个访问结束完全结束后,才能进行下一次访问。无法通过正规途径拓展成异步,因此这里使用了monkey补丁

import requests
from pyquery import PyQuery as pq
import gevent
import time
import gevent.monkey
gevent.monkey.patch_all() words = ['good','bad','cool',
        'hot','nice','better',
        'head','up','down',
        'right','left','east'] def synchronous():
   start = time.time()
   print('同步开始了')
   for word in words:
       print(fetch_word_info(word))
   end = time.time()
   print("同步运行时间: %s 秒" % str(end - start))
   
#执行同步
synchronous()

有道词典网站速度比较慢,基本上半秒解决一个词注释音标问题。那要是3600词就需要半个小时,这速度坑啊!

异步代码

因为requests库在任何时候只允许有一个访问结束完全结束后,才能进行下一次访问。无法通过正规途径拓展成异步,因此这里使用了monkey补丁

import requests
from pyquery import PyQuery as pq
import gevent
import time
import gevent.monkey
gevent.monkey.patch_all() words = ['good','bad','cool',
        'hot','nice','better',
        'head','up','down',
        'right','left','east'] def asynchronous():
   start = time.time()
   print('异步开始了')
   events = [gevent.spawn(fetch_word_info,word) for word in words]
   wordinfos = gevent.joinall(events)
   for wordinfo in wordinfos:
       #获取到数据get方法
       print(wordinfo.get())
   end = time.time()
   print("异步运行时间: %s 秒"%str(end-start)) #执行异步
asynchronous()

这速度,酸爽啊

速度与激情

6.44s vs 0.82s,让我们重新欣赏一会儿这两个动图

项目下载地址

链接: https://pan.baidu.com/s/1eT5gJrO 密码: wad8

gevent:异步理论与实战[转]的更多相关文章

  1. SSIS从理论到实战,再到应用

    原文:SSIS从理论到实战,再到应用 一,是什么(What?) 1.SSIS是Microsoft SQL Server Integration Services的简称,是生成高性能数据集成解决方案(包 ...

  2. SSIS从理论到实战,再到应用(6)----SSIS的自带日志功能

    原文:SSIS从理论到实战,再到应用(6)----SSIS的自带日志功能 上期回顾: SSIS从理论到实战,再到应用(5)----流程控制之Foreach循环 博主最近新负责了一个ssis大项目的架构 ...

  3. SSIS从理论到实战,再到应用(7)----常用的数据类型转换操作

    原文:SSIS从理论到实战,再到应用(7)----常用的数据类型转换操作 上期回顾: SSIS从理论到实战,再到应用(6)----SSIS的自带日志功能 在抽取各种应用的数据时候,经常会遇到数据需要转 ...

  4. SSIS从理论到实战,再到应用(4)----流程控制之For循环

    原文:SSIS从理论到实战,再到应用(4)----流程控制之For循环 上期回顾: SSIS从理论到实战,再到应用(3)----SSIS包的变量,约束,常用容器 在SSIS体系中,控制流可能经常会遇到 ...

  5. SSIS从理论到实战,再到应用(5)----流程控制之Foreach循环

    原文:SSIS从理论到实战,再到应用(5)----流程控制之Foreach循环 上期回顾: SSIS从理论到实战,再到应用(4)----流程控制之For循环 上一期讲了For循环,Foreach循环相 ...

  6. SSIS从理论到实战,再到应用(2)----SSIS包的控制流

    原文:SSIS从理论到实战,再到应用(2)----SSIS包的控制流 前文回顾: SSIS从理论到实战,再到应用(1)----创建自己的第一个包 上次说到创建了自己的第一个包,完成了简单的数据从数据库 ...

  7. SSIS从理论到实战,再到应用(3)----SSIS包的变量,约束,常用容器

    原文:SSIS从理论到实战,再到应用(3)----SSIS包的变量,约束,常用容器 上期回顾: SSIS从理论到实战,再到应用(2)----SSIS包的控制流   首先我们来看看包里面的变量 SSIS ...

  8. SSIS从理论到实战,再到应用(1)----创建自己的第一个包

    原文:SSIS从理论到实战,再到应用(1)----创建自己的第一个包 其实,如果你使用sql2008的导出导入工具的时候,你就已经在使用包了. 目标:使用sql2008 导入导出工具,导入excel数 ...

  9. Docker最全教程——从理论到实战(八)

    在本系列教程中,笔者希望将必要的知识点围绕理论.流程(工作流程).方法.实践来进行讲解,而不是单纯的为讲解知识点而进行讲解.也就是说,笔者希望能够让大家将理论.知识.思想和指导应用到工作的实际场景和实 ...

随机推荐

  1. 【一天一道LeetCode】#15 3Sum

    一天一道LeetCode系列 (一)题目 Given an array S of n integers, are there elements a, b, c in S such that a + b ...

  2. ReentrantReadWriteLock读写锁的使用2

    本文可作为传智播客<张孝祥-Java多线程与并发库高级应用>的学习笔记. 这一节我们做一个缓存系统. 在读本节前 请先阅读 ReentrantReadWriteLock读写锁的使用1 第一 ...

  3. Linux基础正则表达式字符汇整(characters)

    RE 字符 意义与范例 ^word 意义:待搜寻的字串(word)在行首! 范例:搜寻行首为 # 开始的那一行,并列出行号 grep -n '^#' regular_express.txt word$ ...

  4. android的左右侧滑菜单实现

    最近看了很多app应用都采用的是左右侧滑,比如网易新闻.凡客等 这里也试着写一下侧滑 首先看一下效果 然后给出xml布局代码 <RelativeLayout xmlns:android=&quo ...

  5. How tomcat works 读书笔记十四 服务器组件和服务组件

    之前的项目还是有些问题的,例如 1 只能有一个连接器,只能处理http请求,无法添加另外一个连接器用来处理https. 2 对容器的关闭只能是粗暴的关闭Bootstrap. 服务器组件 org.apa ...

  6. MFC中char*,string和CString之间的转换

    MFC中char*,string和CString之间的转换 一.    将CString类转换成char*(LPSTR)类型 方法一,使用强制转换.例如:  CString theString( &q ...

  7. ELF 动态链接 - so 的 .dynamic 段

    动态链接文件中最重要的段就是 .dynamic段 这个段里保存了动态链接器需要的最基本的信息 比如:1.  依赖于哪些共享对象, d_tag = DT_NEED,  d_ptr 表示共享对象文件名 2 ...

  8. python lock, semaphore, event实现线程同步

    lock 机制不管你是java, C#, 还是python都是常用的线程同步机制, 相比较C# 的锁机制, python的加锁显得比较简单, 直接调用threading 标准库的lock 就可以了. ...

  9. miniui 给表格行添加监听事件的几种方法以及点击某列列名数据不能排序的问题

    最近在使用miniui框架做开发,在做表格行的点击监听事件中发现了几个属性,都可以起到监听效果但是执行的结果却大有不同.好了废话不多说,直接上代码. <div id="pageGrid ...

  10. valid palindrome(回文)

    Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignori ...