优点:使用gevent协程,可以更好的利用线程资源。(基于线程实现)

需求:使用一个线程,去请求多个网站的资源(注意,请求上会有延时)<实际上是去请求了大量的网站信息,我们使用了多线程,只不过每个线程依旧会分配到多个网站资源,这里我们只需要去讨论这一条线程即可>

可以看出,由于网络延迟等因素,当我们去获取信息时,有一段时间呗浪费,用于空等信息返回,当我们去获取大量网站的时候,那这个时间是非常大的。我们需要去避免他。

解决方案:使用协程,充分利用我们中间等待的这一段时间,去做其他的事情,比如其请求下一个网站,,或者下几个网站。然后连续去接收信息,就可以充分的利用空耗的时间

1.协程的简单使用:

pip3 install gevent  # gevent模块若是没有,只需要先下载

开始使用:

import gevent
from gevent import monkey

monkry.patch_all()  #可以提高效率
def foo():
print("foo函数开始运行")
gevent.sleep()
print("又回到了foo函数") def bar():
print("bar函数开始运行")
gevent.sleep()
print("又回到了bar函数") gevent.joinall([
gevent.spawn(foo),
gevent.spawn(bar),
])
输出结果:
foo函数开始运行
bar函数开始运行
又回到了foo函数
又回到了bar函数

2.协程的了解:对于上面的例子来说,有点不太容易理解,我们使用计时去了解其中流程,再去讨论上面代码

(1)上面sleep(0)和下面的sleep(3)相比,得出两个函数的执行时间是一致的(几乎是)

import gevent
import time begin = time.time() def foo():
fs = time.time() - begin
print("foo函数开始运行",fs)
gevent.sleep(3) fe = time.time() - begin
print("又回到了foo函数",fe) def bar():
bs = time.time() - begin
print("bar函数开始运行",bs)
gevent.sleep()
be = time.time() - begin
print("又回到了bar函数",be) gevent.joinall([
gevent.spawn(foo),
gevent.spawn(bar),
])
foo函数开始运行 0.01000070571899414
bar函数开始运行 0.01000070571899414
又回到了foo函数 3.0101723670959473
又回到了bar函数 3.0101723670959473

注意输出结果

我们可以看出两个函数都是在统一时间执行第一句输出,在三秒后去执行的第二句输出

(2)sleep(3)和sleep(1)

import gevent
import time begin = time.time() def foo():
fs = time.time() - begin
print("foo函数开始运行",fs)
gevent.sleep() fe = time.time() - begin
print("又回到了foo函数",fe) def bar():
bs = time.time() - begin
print("bar函数开始运行",bs)
gevent.sleep()
be = time.time() - begin
print("又回到了bar函数",be) gevent.joinall([
gevent.spawn(foo),
gevent.spawn(bar),
])

注意输出结果:几乎在同一时间执行两个函数(顺序和joinall方法中注册顺序有关),在我们设定的sleep时间后去继续执行函数

foo函数开始运行 0.0060002803802490234
bar函数开始运行 0.0060002803802490234
又回到了foo函数 1.0060575008392334
又回到了bar函数 3.006171941757202

所以说对于最上面简单使用中的执行顺序先是根据joinall的注册顺序去打印

foo函数开始运行
bar函数开始运行

然后由于sleep(0)间隔是0,所以立即去执行下面的打印程序(当sleep的时间是一致时,顺序还是和注册时一致)

又回到了foo函数
又回到了bar函数

(3)使用time.sleep()去更加深刻了解协程

import gevent
import time begin = time.time() def foo():
fs = time.time() - begin
print("foo函数开始运行",fs)
gevent.sleep()
time.sleep()  #这里睡眠4秒 fe = time.time() - begin
print("又回到了foo函数",fe) def bar():
bs = time.time() - begin
print("bar函数开始运行",bs)
gevent.sleep()
be = time.time() - begin
print("又回到了bar函数",be) gevent.joinall([
gevent.spawn(foo),
gevent.spawn(bar),
])

注意输出结果:发现对于我们在foo中设置的time.sleep(4)对bar方法也有影响。

foo函数开始运行 0.005000114440917969
bar函数开始运行 0.0060002803802490234
又回到了foo函数 5.006286144256592
又回到了bar函数 5.007286310195923

原因:gevent设置了我们协程的苏醒时间,但是当苏醒时间与我们的执行时间相冲突,那么会以执行时间为主(毕竟这是单线程,不会考虑其他的),而原来的设置的gevent.sleep(秒数)则变成了大小比较,谁在后,谁就后执行

任务框架:

import gevent
import time begin = time.time() def foo(url,index):
fs = time.time() - begin
print("%s:发送请求到%s,等待返回"%(index,url),fs) #这里可以模拟发送请求
gevent.sleep()
fe = time.time() - begin
print("%s:获取信息从%s,开始处理"%(index,url),fe) #这里模拟处理信息 gevent.joinall([
gevent.spawn(foo,"www.baidu.com",1),  #注意传参方式
gevent.spawn(foo,"www.sina.com.sn",2),
])

输出结果:

:发送请求到www.baidu.com,等待返回 0.005000114440917969
:发送请求到www.sina.com.sn,等待返回 0.005000114440917969
:获取信息从www.baidu.com,开始处理 0.005000114440917969
:获取信息从www.sina.com.sn,开始处理 0.005000114440917969


补充:greenlet协程(gevent是基于greenlet实现,所以有必要去了解下)

from greenlet import greenlet

def foo():
print("开始执行foo")
gr2.switch()
print("又回到foo")
gr2.switch() def bar():
print("开始执行bar")
gr1.switch()
print("又回到bar") gr1 = greenlet(foo)
gr2 = greenlet(bar)
gr1.switch() #以gr1开始执行,switch中也可以传递参数

输出结果:

开始执行foo
开始执行bar
又回到foo
又回到bar

python---基础知识回顾(十)进程和线程(协程gevent:线程在I/O请求上的优化)的更多相关文章

  1. python基础之进程间通信、进程池、协程

    进程间通信 进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的. 进程队列queue 不同于线程queue,进程 ...

  2. python爬虫主要就是五个模块:爬虫启动入口模块,URL管理器存放已经爬虫的URL和待爬虫URL列表,html下载器,html解析器,html输出器 同时可以掌握到urllib2的使用、bs4(BeautifulSoup)页面解析器、re正则表达式、urlparse、python基础知识回顾(set集合操作)等相关内容。

    本次python爬虫百步百科,里面详细分析了爬虫的步骤,对每一步代码都有详细的注释说明,可通过本案例掌握python爬虫的特点: 1.爬虫调度入口(crawler_main.py) # coding: ...

  3. python基础知识回顾之列表

    在python 中,主要的常用数据类型有列表,元组,字典,集合,字符串.对于这些基础知识,应该要能够足够熟练掌握. 如何创建列表: # 创建一个空列表:定义一个变量,然后在等号右边放一个中括号,就创建 ...

  4. Python开发基础-Day32 进程间通信、进程池、协程

    进程间通信 进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的. 进程队列queue 不同于线程queue,进程 ...

  5. python基础知识回顾之字符串

    字符串是python中使用频率很高的一种数据类型,内置方法也是超级多,对于常用的方法,还是要注意掌握的. #author: Administrator #date: 2018/10/20 # pyth ...

  6. python基础知识回顾之元组

    元组与列表的方法基本一样,只不过创建元组是用小括号()把元素括起来,两者的区别在于,元组的元素不可被修改. 元组被称为只读列表,即数据可以被查询,但不能被修改,列表的切片操作适用于元组. 元组写在小括 ...

  7. python基础知识回顾[1]

    1.声明变量 # 声明一个变量name用来存储一个字符串'apollo' name = 'apollo' # 声明一个变量age用来存储一个数字20 age = 20 # 在控制台打印变量name中存 ...

  8. C#基础知识回顾--线程传参

    C#基础知识回顾--线程传参 在不传递参数情况下,一般大家都使用ThreadStart代理来连接执行函数,ThreadStart委托接收的函数不能有参数, 也不能有返回值.如果希望传递参数给执行函数, ...

  9. Windows内核基础知识-8-监听进程、线程和模块

    Windows内核基础知识-8-监听进程.线程和模块 Windows内核有一种强大的机制,可以在重大事件发送时得到通知,比如这里的进程.线程和模块加载通知. 本次采用链表+自动快速互斥体来实现内核的主 ...

  10. Python 基础知识(一)

    1.Python简介 1.1.Python介绍 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆(中文名字:龟叔)为了在阿姆斯特丹打发时 ...

随机推荐

  1. iOS 开发学习-import和include的区别

    //当我们在代码中使用两次#include的时候会报错:因为#include相当于拷贝头文件中的声明内容,所以会报重复定义的错误 //但是使用两次#import的话,不会报错,所以他可以解决重复导入的 ...

  2. JavaScript实现大整数减法

    继上一篇博文写了大整数加法之后,我又模拟上篇博文的算法,自己实现了大整数减法. 大整数减法相对于加法来说,稍微复杂一点.由于要考虑一些情况: 1. 两个数相减,可能会出现结果为正.负和0三种情况: 2 ...

  3. Git管理分支

    管理分支:git branch 直至现在为止,我们的项目版本库一直都是只有一个分支 master.在 git 版本库中创建分支的成本几乎为零,所以,不必吝啬多创建几个分支.下面列举一些常见的分支策略, ...

  4. python learning1.py

    # 廖雪峰的官方网站 python教材 1~4章 # 格式控制符语法 print('Hello, %s' % 'world') print('hello, %s, you have %d dollar ...

  5. 信安实践——CSRF攻击与防御

    1.实验原理 CSRF(Cross-Site Request Forgery,跨站点伪造请求)是一种网络攻击方式,该攻击可以在受害者毫不知情的情况下以受害者名义伪造请求发送给受攻击站点,从而在未授权的 ...

  6. lintcode-514-栅栏染色

    514-栅栏染色 我们有一个栅栏,它有n个柱子,现在要给柱子染色,有k种颜色可以染. 必须保证不存在超过2个相邻的柱子颜色相同,求有多少种染色方案. 注意事项 n和k都是非负整数 样例 n = 3, ...

  7. 简单实现wc.exe软件基本功能

    简单实现wc.exe软件基本功能 软件需求分析: 一.基本功能 支持 -c  统计字符数(char count) 支持 -w  统计单词数(word count) 支持 -l  统计总行数(line ...

  8. [并查集] 1107. Social Clusters (30)

    1107. Social Clusters (30) When register on a social network, you are always asked to specify your h ...

  9. mysql中enum类型

    ENUM类型 ENUM 是一个字符串对象,其值通常选自一个允许值列表中,该列表在表创建时的列规格说明中被明确地列举. 在下列某些情况下,值也可以是空串("") 或 NULL: 如果 ...

  10. 栈java实现

    这几天,过得挺充实的,每天都在不停的上课,早上很早就起来去跑步,晚上到图书馆看书.一边紧张的学习,一边在默默的备战软考.最近还接手了一个公司官网的建设.这是我在川信最后的一个完整学期了,每件事我都要认 ...