浅谈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实现mysql的单引号字符串过滤方法
Nov 14 Python
python3使用PyMysql连接mysql数据库实例
Feb 07 Python
详解Python安装scrapy的正确姿势
Jun 26 Python
PyCharm在新窗口打开项目的方法
Jan 17 Python
pthon贪吃蛇游戏详细代码
Jan 27 Python
在Python中等距取出一个数组其中n个数的实现方式
Nov 27 Python
python接口自动化如何封装获取常量的类
Dec 24 Python
OpenCV Python实现拼图小游戏
Mar 23 Python
Python如何用wx模块创建文本编辑器
Jun 07 Python
pyCharm 设置调试输出窗口中文显示方式(字符码转换)
Jun 09 Python
如何让python的运行速度得到提升
Jul 08 Python
用python制作个音乐下载器
Jan 30 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程序
2006/10/09 PHP
一个PHP的ZIP压缩类分享
2014/05/04 PHP
PHP中字符安全过滤函数使用小结
2015/02/25 PHP
PHP脚本监控Nginx 502错误并自动重启php-fpm
2015/05/13 PHP
PHP基于回溯算法解决n皇后问题的方法示例
2017/11/07 PHP
Prototype使用指南之form.js
2007/01/10 Javascript
又一个小巧的图片预加载类
2007/05/05 Javascript
JS去除右边逗号的简单方法
2013/07/03 Javascript
javascript中expression的用法整理
2014/05/13 Javascript
nodejs教程之异步I/O
2014/11/21 NodeJs
PHP结合jQuery实现的评论顶、踩功能
2015/07/22 Javascript
js实现仿网易点击弹出提示同时背景变暗效果
2015/08/13 Javascript
JS仿淘宝实现的简单滑动门效果代码
2015/10/14 Javascript
BootStrap Progressbar 实现大文件上传的进度条的实例代码
2016/06/27 Javascript
jquery实现跳到底部,回到顶部效果的简单实例(类似锚)
2016/07/10 Javascript
使用ionic切换页面卡顿的解决方法
2016/12/16 Javascript
laravel5.4+vue+element简单搭建的示例代码
2017/08/29 Javascript
微信小程序实现多选删除列表数据功能示例
2019/01/15 Javascript
React 组件渲染和更新的实现代码示例
2019/02/21 Javascript
初试vue-cli使用HBuilderx打包app的坑
2019/07/17 Javascript
使用Angular9和TypeScript开发RPG游戏的方法
2020/03/25 Javascript
python socket多线程通讯实例分析(聊天室)
2016/04/06 Python
Python编程实战之Oracle数据库操作示例
2017/06/21 Python
Python3实现爬取简书首页文章标题和文章链接的方法【测试可用】
2018/12/11 Python
Python 使用Numpy对矩阵进行转置的方法
2019/01/28 Python
pyqt5 comboBox获得下标、文本和事件选中函数的方法
2019/06/14 Python
Python实现滑动平均(Moving Average)的例子
2019/08/24 Python
Python内置加密模块用法解析
2019/11/25 Python
Python解释器以及PyCharm的安装教程图文详解
2020/02/26 Python
Python使用re模块验证危险字符
2020/05/21 Python
手把手教你将Flask应用封装成Docker服务的实现
2020/08/19 Python
面向对象编程OOP的优点
2013/01/22 面试题
幼儿园教师考核制度
2014/02/01 职场文书
大学毕业寄语大全
2014/04/10 职场文书
css 中多种边框的实现小窍门
2021/04/07 HTML / CSS
关于mysql中string和number的转换问题
2022/06/14 MySQL