浅谈Python协程asyncio


Posted in Python onJune 20, 2021

一、协程

官方描述;
协程是子例程的更一般形式。 子例程可以在某一点进入并在另一点退出。 协程则可以在许多不同的点上进入、退出和恢复。 它们可通过 async def 语句来实现。 参见 PEP 492。

  • 协程不是计算机内部提供的,不像进程、线程,由电脑本身提供,它是由程序员人为创造的, 实现函数异步执行。
  • 协程(Coroutine),也可以被称为微线程,是一种用户太内的上下文切换技术,其实就是通过一个线程实现代码块相互切换执行。看上去像子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。例如:
# 需要python3.7+
import asyncio


async def main():
    print('hello')
    await asyncio.sleep(1)
    print('world')

asyncio.run(main())

# 打印 "hello",等待 1 秒,再打印 "world"

注意:简单地调用一个协程并不会使其被调度执行,

直接main() 调用会有问题:

RuntimeWarning: coroutine 'main' was never awaited
  main()
RuntimeWarning: Enable tracemalloc to get the object allocation traceback

 

def func1():
    print(1)
    ...
    print(2)
    
def func2():
    print(3)
    ...
    print(4)

func1()
func2() 

# 结果:1 2 3 4

实现协程的方法:

greenlet,早期模块【不建议使用】

yield关键字,它是python的生成器,具有保存状态,切换到其他函数去执行,再切换回原函数的功能。

asyncio装饰器(python3.4引入)

async、await 关键字(python3.5)【推荐使用】

1.1 greenlet实现协程

# 第三方模块,因此需要安装

pip install greenlet
from greenlet import greenlet


def func1():
    print(1)
    gr2.switch()
    print(2)
    gr2.switch()


def func2():
    print(3)
    gr1.switch()
    print(4)


gr1 = greenlet(func1)
gr2 = greenlet(func2)

gr1.switch()

# 结果:1 3 2 4

1.2 yield关键字

def func1():
    yield 1
    yield from func2()
    yield 2


def func2():
    yield 3
    yield 4

f1 = func1()
for item in f1:
    print(item)
    
# 结果:1 3 2 4

1.3 asynico装饰器

python3.4 及之后版本支持

DeprecationWarning: “@coroutine” decorator is deprecated since Python 3.8, use “async def”
翻译:@coroutine"装饰器自Python 3.8起已弃用,请使用"async def"代替

所以这个也不支持。

import asyncio

@asyncio.coroutine
def func1():
    print(1)
    yield from asyncio.sleep(2)  # 遇到IO耗时操作,自动切换到tasks中其他任务,比如:网络IO,下载图片
    print(2)

@asyncio.coroutine
def func2():
    print(3)
    yield from asyncio.sleep(2)  # 遇到IO耗时操作,自动切换到tasks中其他任务,比如:网络IO,下载图片
    print(4)

tasks = [
    asyncio.ensure_future(func1()),
    asyncio.ensure_future(func2())
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

# 结果: 1 3 2 4

1.4 async & await 关键字

import asyncio


async def func1():
    print(1)
    await asyncio.sleep(2)  # 遇到IO耗时操作,自动切换到tasks中其他任务,比如:网络IO,下载图片
    print(2)


async def func2():
    print(3)
    await asyncio.sleep(2)  # 遇到IO耗时操作,自动切换到tasks中其他任务,比如:网络IO,下载图片
    print(4)

tasks = [
    asyncio.ensure_future(func1()),
    asyncio.ensure_future(func2())
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

二、协程的意义

充分利用线程。在一个线程中如果遇到IO等待时间线程不会一直等待,利用空闲时间再去干点其他事情。

以下载三张图片为例:

普通方式(同步)下载:

import time
import requests

def download_image(url, img_name):
    print("开始下载:", url)
    # 发送网络请求,下载图片
    response = requests.get(url)
    print("下载完成")
    # 图片保存到本地文件
    file_name = str(img_name) + '.png'
    with open(file_name, mode='wb') as file:
        file.write(response.content)

if __name__ == '__main__':
    start = time.time()
    url_list = [
        'https://tse4-mm.cn.bing.net/th/id/OIP.866vRxQ8QvyDsrUuXiu7qwHaNK?w=182&h=324&c=7&o=5&pid=1.7',
        'https://tse2-mm.cn.bing.net/th/id/OIP.HUcWtoYPG-z2pu4ityajbAHaKQ?w=182&h=252&c=7&o=5&pid=1.7',
        'https://tse2-mm.cn.bing.net/th/id/OIP.MvncR0-Pt9hVxKTdrvD9dAHaNK?w=182&h=324&c=7&o=5&pid=1.7',
        'https://tse1-mm.cn.bing.net/th/id/OIP._nGloaeMWbL7NB7Lp6SnXQHaLH?w=182&h=273&c=7&o=5&pid=1.7',
        ]
    img_name = 1
    for item in url_list:
        download_image(item, img_name)
        img_name += 1
    end = time.time()
    print(end - start)
    
 # 最终时间:7.25s

协程方式(异步)下载:

import aiohttp
import asyncio
import time


async def fetch(session, url):
    print("发送请求:", url)

    async with session.get(url, verify_ssl=False) as response:
        content = await response.content.read()
        file_name = url.rsplit('_')[-1]
        # print(file_name)
        with open(file_name, mode='wb') as file_object:
            file_object.write(content)
        print("下载完成")


async def main():
    async with aiohttp.ClientSession() as session:
        url_list = [
            'https://www3.autoimg.cn/newsdfs/g26/M02/35/A9/120x90_0_autohomecar__ChsEe12AXQ6AOOH_AAFocMs8nzU621.jpg',
            'https://www3.autoimg.cn/newsdfs/g26/M02/35/A9/120x90_0_autohomecar__ChsEe12AXQ6AOOH_AAFocMs8nzU621.jpg',
            'https://www3.autoimg.cn/newsdfs/g26/M02/35/A9/120x90_0_autohomecar__ChsEe12AXQ6AOOH_AAFocMs8nzU621.jpg',
            'https://www3.autoimg.cn/newsdfs/g26/M02/35/A9/120x90_0_autohomecar__ChsEe12AXQ6AOOH_AAFocMs8nzU621.jpg',
        ]
        tasks = [asyncio.ensure_future(fetch(session, url)) for url in url_list]
        await asyncio.wait(tasks)

if __name__ == '__main__':
    start = time.time()
    asyncio.get_event_loop().run_until_complete(main())
    end = time.time()
    print(end - start)
    
# 结果:0.05s

到此这篇关于浅谈Python协程的文章就介绍到这了,更多相关Python协程内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
全面了解python字符串和字典
Jul 07 Python
python 中random模块的常用方法总结
Jul 08 Python
python如何拆分含有多种分隔符的字符串
Mar 20 Python
详解django.contirb.auth-认证
Jul 16 Python
解决python中 f.write写入中文出错的问题
Oct 31 Python
python根据url地址下载小文件的实例
Dec 18 Python
Python实现点阵字体读取与转换的方法
Jan 29 Python
python中open函数的基本用法示例
Sep 07 Python
Django ModelForm操作及验证方式
Mar 30 Python
关于Keras Dense层整理
May 21 Python
python简单实现9宫格图片实例
Sep 03 Python
python pygame入门教程
Jun 01 Python
Python3接口性能测试实例代码
Jun 20 #Python
使用Djongo模块在Django中使用MongoDB数据库
python自动计算图像数据集的RGB均值
详解如何用Python实现感知器算法
python中24小时制转换为12小时制的方法
Jun 18 #Python
用Python selenium实现淘宝抢单机器人
python中pandas对多列进行分组统计的实现
You might like
《魔兽世界》惊魂幻象将获得调整
2020/03/08 其他游戏
关于PHP中字符串与多进制转换函数的实例代码
2016/11/03 PHP
PHP使用imagick扩展实现合并图像的方法
2017/04/25 PHP
PHP实现对图片的反色处理功能【测试可用】
2018/02/01 PHP
用javascript连接access数据库的方法
2006/11/17 Javascript
js中将多个语句写成一个语句的两种方法小结
2007/12/08 Javascript
用javascript getComputedStyle获取和设置style的原理
2008/10/10 Javascript
JavaScript中的稀疏数组与密集数组[译]
2012/09/17 Javascript
简单分析javascript面向对象与原型
2015/05/21 Javascript
jquery实现简单的全选和反选功能
2016/01/02 Javascript
用jQuery的AJax实现异步访问、异步加载
2016/11/02 Javascript
javascript输出AscII码扩展集中的字符方法
2016/12/26 Javascript
vue 引入公共css文件的简单方法(推荐)
2018/01/20 Javascript
使用electron制作满屏心特效的示例代码
2018/11/27 Javascript
Vue+Node实现的商城用户管理功能示例
2019/12/23 Javascript
[08:02]DOTA2牵红线 zhou神抱得美人归
2014/03/22 DOTA
[52:22]EG vs VG Supermajor小组赛B组 BO3 第一场 6.2
2018/06/03 DOTA
[41:11]完美世界DOTA2联赛PWL S2 Inki vs Magma 第一场 11.22
2020/11/24 DOTA
讲解Python中运算符使用时的优先级
2015/05/14 Python
详解Python3中的Sequence type的使用
2015/08/01 Python
django 常用orm操作详解
2017/09/13 Python
用tensorflow实现弹性网络回归算法
2018/01/09 Python
python3+dlib实现人脸识别和情绪分析
2018/04/21 Python
50行Python代码获取高考志愿信息的实现方法
2019/07/23 Python
Django+zTree构建组织架构树的方法
2019/08/21 Python
运行tensorflow python程序,限制对GPU和CPU的占用操作
2020/02/06 Python
python中读入二维csv格式的表格方法详解(以元组/列表形式表示)
2020/04/24 Python
python中判断文件结束符的具体方法
2020/08/04 Python
关于 HTML5 的七个传说小结
2012/04/12 HTML / CSS
HTML5中drawImage用法分析
2014/12/01 HTML / CSS
介绍一下如何利用路径遍历进行攻击及如何防范
2014/01/19 面试题
英智兴达软件测试笔试题
2016/10/12 面试题
介绍一下except的用法和作用
2015/01/22 面试题
会计找工作求职信范文
2013/12/09 职场文书
农林经济管理专业自荐信
2014/09/01 职场文书
JS前端监控采集用户行为的N种姿势
2022/07/23 Javascript