浅谈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实现通过代理服务器访问远程url的方法
Apr 29 Python
Python的time模块中的常用方法整理
Jun 18 Python
django1.8使用表单上传文件的实现方法
Nov 04 Python
python3读取csv和xlsx文件的实例
Jun 22 Python
django 实现电子支付功能的示例代码
Jul 25 Python
Python中extend和append的区别讲解
Jan 24 Python
python pickle存储、读取大数据量列表、字典数据的方法
Jul 07 Python
Python利用WMI实现ping命令的例子
Aug 14 Python
python 求定积分和不定积分示例
Nov 20 Python
Python GUI库PyQt5样式QSS子控件介绍
Feb 25 Python
Python实现区域填充的示例代码
Feb 03 Python
在python中读取和写入CSV文件详情
Jun 28 Python
Python3接口性能测试实例代码
Jun 20 #Python
使用Djongo模块在Django中使用MongoDB数据库
python自动计算图像数据集的RGB均值
详解如何用Python实现感知器算法
python中24小时制转换为12小时制的方法
Jun 18 #Python
用Python selenium实现淘宝抢单机器人
python中pandas对多列进行分组统计的实现
You might like
php生成excel列序号代码实例
2013/12/24 PHP
thinkphp3.2.2实现生成多张缩略图的方法
2014/12/19 PHP
详解PHP中array_rand函数的使用方法
2016/09/11 PHP
PHP基于mssql扩展远程连接MSSQL的简单实现方法
2016/10/08 PHP
php array_multisort 对数组进行排序详解及实例代码
2016/10/27 PHP
php Session无效分析资料整理
2016/11/29 PHP
PHP htmlspecialchars() 函数实例代码及用法大全
2018/09/18 PHP
利用javascript解决图片缩放及其优化的代码
2012/05/23 Javascript
jQuery建立一个按字母顺序排列的友好页面索引(兼容IE6/7/8)
2013/02/26 Javascript
拖动table标题实现改变td的大小(css+js代码)
2013/04/16 Javascript
jQuery预加载图片常用方法
2015/06/15 Javascript
JS简单实现String转Date的方法
2016/03/02 Javascript
JavaScript第一篇之实现按钮全选、功能
2016/08/21 Javascript
JS中this上下文对象使用方式
2016/10/09 Javascript
jQuery插件ContextMenu自定义图标
2017/03/15 Javascript
解决vue v-for 遍历循环时key值报错的问题
2018/09/06 Javascript
jQuery+PHP实现上传裁剪图片
2020/06/29 jQuery
js中Generator函数的深入讲解
2019/04/07 Javascript
使用Node.js在深度学习中做图片预处理的方法
2019/09/18 Javascript
浅谈Vue使用Cascader级联选择器数据回显中的坑
2020/10/31 Javascript
JavaScript中常用的3种弹出提示框(alert、confirm、prompt)
2020/11/10 Javascript
JS实现纸牌发牌动画
2021/01/19 Javascript
[01:21]辉夜杯战队访谈宣传片—CDEC
2015/12/25 DOTA
python实现按首字母分类查找功能
2019/10/31 Python
python列表推导式操作解析
2019/11/26 Python
用python3读取python2的pickle数据方式
2019/12/25 Python
佳能英国官方网站:Canon UK
2017/08/08 全球购物
Qoo10台湾站:亚洲领先的在线市场
2018/05/15 全球购物
MAC Cosmetics官方网站:魅可专业艺术彩妆
2019/04/10 全球购物
GWT都有什么特性
2016/12/02 面试题
英语商务邀请函范文
2014/01/16 职场文书
公司应聘求职信
2014/06/21 职场文书
学生保证书
2015/01/16 职场文书
综合办公室岗位职责
2015/04/11 职场文书
2015年六年级班主任工作总结
2015/10/15 职场文书
Windows Server 2012 R2 磁盘分区教程
2022/04/29 Servers