8.8 协程

​ 我们都知道线程间的任务切换是由操作系统来控制的,而协程的出现,就是为了减少操作系统的开销,由协程来自己控制任务的切换

​ 协程本质上就是线程。既然能够切换任务,所以线程有两个最基本的功能:一是保存状态;二是任务切换

8.8.1 协程的特点

【优点】

  • 线程任务切换开销小,属于程序级的切换,操作系统感知不到
  • 单线程内就可以实现并发的效果,最大限度的利用CPU

【缺点】

  • 协程的本质是单线程,无法利用多核;可以一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程
  • 协程是单个线程执行多个任务,一旦协程遇到阻塞,将会阻塞整个线程

【特点】

  • 必须要在单线程中实现并发
  • 修改共享数据不需要加锁
  • 用户程序内保存多个控制流的上下文栈
  • 一个协程遇到IO操作自动切换到其它协程

8.8.2 Greenlet

使用grennlet第三方库实现任务间的切换

from greenlet import greenlet

def get_money(name):
print(f"{name} get 10 $")
g2.switch('jiawen')
print(f"{name} get 20 $")
g2.switch() def buy_goods(name):
print(f"{name} buy no.1 good ")
g1.switch()
print(f"{name} buy no.2 good ")
g1.switch() if __name__ == '__main__':
g1 = greenlet(get_money)
g2 = greenlet(buy_goods) g1.switch('gailun') # switch在第一次时必须要传入参数,以后就不需要了

效率对比

from greenlet import greenlet
import time def f1():
re = 1
for i in range(10000000):
re *= i
g2.switch() def f2():
re = 1
for i in range(10000000):
re += i
g1.switch() if __name__ == '__main__':
start = time.time()
g1 = greenlet(f1)
g2 = greenlet(f2) g1.switch() # switch在第一次时必须要传入参数,以后就不需要了
print(f'{time.time()- start}') # 5.822627305984497 import time def f1():
re = 1
for i in range(10000000):
re *= i def f2():
re = 1
for i in range(10000000):
re += i start = time.time()
f1()
f2()
print(f'{time.time()- start}') # 1.041489601135254

【结论】单纯的切换,在没有IO阻塞的情况下,协程的效率反而降低

8.8.3 Gevent介绍

Gevent也是一个第三方库,主要用来实现并发同步或是异步编程,在gevent中用到的主要模式是Greenlet, Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。

【方法】

​ gevent.spawn(func,*args,**kwargs) spawn括号内第一个参数是函数名,后面可以有多个参数,可以是位置实参或关键字实参,都是传给函数func,spawn是异步提交任务

​ join() 等待调用者结束

​ value() 拿到调用者的返回值

遇到IO阻塞会自动切换

import gevent

def get_money(name):
print(f"{name} get 10 $")
gevent.sleep(2) #模拟的是gevent可以识别的IO阻塞
print(f"{name} get 20 $") def buy_goods(name):
print(f"{name} buy no.1 good ")
gevent.sleep(1)
print(f"{name} buy no.2 good ") if __name__ == '__main__':
g1 = gevent.spawn(get_money,'gailun')
g2 = gevent.spawn(buy_goods,name='jiawen')
g1.join()
g2.join()
# gevent.joinall([g1,g2])
print('__main__')
# 输出
gailun get 10 $
jiawen buy no.1 good
jiawen buy no.2 good
gailun get 20 $
__main__

【注意】如果要使gevent识别所有的io阻塞,放到被打补丁者的前面或者直接写在在文件的最开头写上以下代码

rom gevent import monkey;monkey.patch_all()

应用

# 爬虫
from gevent import monkey;monkey.patch_all()
import gevent
import requests
import time def get_inf(url):
print(f'GET:{url}')
res = requests.get(url)
if res.status_code == 200:
print(f"{len(res.text)} get from {url}") if __name__ == '__main__':
start_time = time.time()
gevent.joinall(
[gevent.spawn(get_inf,'https://www.python.org/'),
gevent.spawn(get_inf,'https://www.yahoo.com/'),
gevent.spawn(get_inf,'https://github.com/'),
]
)
print(f"take {time.time()-start_time} secondes")

Python学习之协程的更多相关文章

  1. Python学习---线程/协程/进程学习 1220【all】

    Python学习---线程基础学习 Python学习---线程锁/信号量/条件变量同步1221 Python学习---同步条件event/队列queue1223 Python学习---进程 1225 ...

  2. python学习笔记 协程

    在学习异步IO模型前,先来了解协程 协程又叫做微线程,Coroutine 子程序或者成为函数,在所有语言中都是层级调用,比如a调用b,b调用c.c执行完毕返回,b执行完毕返回,最后a执行完毕返回 所以 ...

  3. python学习之-- 协程

    协程(coroutine)也叫:微线程,是一种用户态的轻量级线程,就是在单线程下实现并发的效果.优点:1:无需线程上下文切换的开销.(就是函数之间来回切换)2:无需原子操作锁定及同步的开销.(如改一个 ...

  4. Python学习笔记--协程asyncio

    协程的主要功能是单线程并发运行 假设有3个耗时不一样的任务.看看协程的效果. 先来看没有使用协程情况: #!/usr/bin/python3 # -*- coding:utf-8 -*- import ...

  5. [转载]Python 3.5 协程究竟是个啥

    http://blog.rainy.im/2016/03/10/how-the-heck-does-async-await-work-in-python-3-5/ [译] Python 3.5 协程究 ...

  6. [译] Python 3.5 协程究竟是个啥

    转自:http://blog.rainy.im/2016/03/10/how-the-heck-does-async-await-work-in-python-3-5/ [译] Python 3.5 ...

  7. Python基础之协程

    阅读目录 一 引子 二 协程介绍 三 Greenlet模块 四 Gevent模块 引子 之前我们学习了线程.进程的概念,了解了在操作系统中 进程是资源分配的最小单位,线程是CPU调度的最小单位. 按道 ...

  8. python中和生成器协程相关的yield之最详最强解释,一看就懂(一)

    yield是python中一个非常重要的关键词,所有迭代器都是yield实现的,学习python,如果不把这个yield的意思和用法彻底搞清楚,学习python的生成器,协程和异步io的时候,就会彻底 ...

  9. python基础(35):协程

    1. 前言 之前我们学习了线程.进程的概念,了解了在操作系统中进程是资源分配的最小单位,线程是CPU调度的最小单位.按道理来说我们已经算是把cpu的利用率提高很多了.但是我们知道无论是创建多进程还是创 ...

随机推荐

  1. BZOJ1233 [Usaco2009Open]干草堆tower[贪心+单调队列优化]

    地址 注意思路!多看几遍! 很巧妙的一道题.不再是决策点以dp值中一部分含j项为维护对象,而是通过维护条件来获取决策. 首先有个贪心策略,让底层的宽度尽可能小,才能让高度尽可能高.所以应该倒着dp,表 ...

  2. 【洛谷P4309】最长上升子序列

    题目大意:给定一个序列,初始为空.现在我们将 1 到 N 的数字插入到序列中,每次将一个数字插入到一个特定的位置.每插入一个数字,我们都想知道此时最长上升子序列长度是多少? 题解:学会了 rope 操 ...

  3. DevExpress ASP.NET Core v19.1版本亮点:Visual Studio集成

    行业领先的.NET界面控件DevExpress 发布了v19.1版本,本文将以系列文章的方式为大家介绍DevExpress ASP.NET Core Controls v19.1中新增的一些控件及增强 ...

  4. 让IE8和IE9支持 placeholder

    1.原因:placeholder是h5的新属性,IE10以前的浏览器(8.9)不支持此属性. 2.解决方法:jQuery三方插件  jquery-placeholder 3.快速开始: <!DO ...

  5. Python CGI编程Ⅶ

    简单的表单实例:GET方法 以下是一个通过HTML的表单使用GET方法向服务器发送两个数据,提交的服务器脚本同样是hello_get.py文件,hello_get.html 代码如下: 默认情况下 c ...

  6. Python CGI编程Ⅴ

    通过CGI程序传递 Textarea 数据 Textarea 向服务器传递多行数据,HTML代码如下: textarea.py 脚本代https://www.xuanhe.net/码如下: 修改 te ...

  7. 自定义 Swiper 的上一页,下一页按钮

    1. Swiper 的上一页,下一页按钮,不是必需包含在container 中的 2. 定义上一页,下一页按钮的样式,CSS略.... 3. 在初始化Swiper中,定义上一页,下一页按钮

  8. BZOJ 2870: 最长道路tree 树的直径+并查集

    挺好的一道题. 把所有点都离线下来,一个个往里加入就行了. #include <cstdio> #include <algorithm> #define N 100003 #d ...

  9. 10.Python内置函数一览表

    为了提高程序员的开发效率,Python 提供了很多可以直接拿来用的函数(初学者可以先理解为方法),每个函数都可以帮助程序员实现某些具体的功能. 举个例子,在 Python 2.x 中 print 只是 ...

  10. Ubuntu18.04安装rabbitvcs svn图形化客户端和简单实用

    1.1  自带source源里面查找rabbitvcs信息 sudo apt search rabbitvcs 1.2  安装rabbitvcs sudo apt install rabbitvcs- ...