浅谈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之一个免费的实验室
Sep 14 Python
Python读取环境变量的方法和自定义类分享
Nov 22 Python
python使用PIL给图片添加文字生成海报示例
Aug 17 Python
python学生信息管理系统(完整版)
Apr 05 Python
Python数据分析:手把手教你用Pandas生成可视化图表的教程
Dec 15 Python
实例详解python函数的对象、函数嵌套、名称空间和作用域
May 31 Python
Django 外键的使用方法详解
Jul 19 Python
Python爬虫之urllib基础用法教程
Oct 12 Python
Python3实现配置文件差异对比脚本
Nov 18 Python
Python实现剪刀石头布小游戏(与电脑对战)
Dec 31 Python
win10安装tensorflow-gpu1.8.0详细完整步骤
Jan 20 Python
Python利用PyPDF2库获取PDF文件总页码实例
Apr 03 Python
Python3接口性能测试实例代码
Jun 20 #Python
使用Djongo模块在Django中使用MongoDB数据库
python自动计算图像数据集的RGB均值
详解如何用Python实现感知器算法
python中24小时制转换为12小时制的方法
Jun 18 #Python
用Python selenium实现淘宝抢单机器人
python中pandas对多列进行分组统计的实现
You might like
人尽可用的Windows技巧小贴士之下篇
2007/03/22 PHP
win7计划任务定时执行PHP脚本设置图解
2014/05/09 PHP
thinkphp获取栏目和文章当前位置的方法
2014/10/29 PHP
PHP中一些可以替代正则表达式函数的字符串操作函数
2014/11/17 PHP
PHP实现通过Luhn算法校验信用卡卡号是否有效
2015/03/23 PHP
Laravel 5 框架入门(一)
2015/04/09 PHP
共享自己写一个框架DreamScript
2007/01/20 Javascript
JavaScript 面向对象之命名空间
2010/05/04 Javascript
IE6下CSS图片缓存问题解决方法
2010/12/09 Javascript
javascript禁制后退键(Backspace)实例代码
2013/11/15 Javascript
jQuery学习笔记之jQuery原型属性和方法
2014/06/09 Javascript
JavaScript数组的一些奇葩行为
2016/01/25 Javascript
javascript中FOREACH数组方法使用示例
2016/03/01 Javascript
JS组件Bootstrap dropdown组件扩展hover事件
2016/04/17 Javascript
使用plupload自定义参数实现多文件上传
2016/07/19 Javascript
自己封装的一个原生JS拖动方法(推荐)
2016/11/22 Javascript
深入理解javascript中concat方法
2016/12/12 Javascript
详解JS浏览器事件循环机制
2019/03/27 Javascript
Vue执行方法,方法获取data值,设置data值,方法传值操作
2020/08/05 Javascript
js通过canvas生成图片缩略图
2020/10/02 Javascript
对Python 2.7 pandas 中的read_excel详解
2018/05/04 Python
Python 中的lambda函数介绍
2018/10/10 Python
Python实现从SQL型数据库读写dataframe型数据的方法【基于pandas】
2019/03/18 Python
在django项目中导出数据到excel文件并实现下载的功能
2020/03/13 Python
Python数据正态性检验实现过程
2020/04/18 Python
css3使网页、图片变成灰色兼容大多数浏览器
2014/07/02 HTML / CSS
HTML5拖拽文件上传的示例代码
2021/03/04 HTML / CSS
TUMI澳大利亚网站:美国旅行箱包品牌
2017/03/27 全球购物
甜品蛋糕店创业计划书范文
2014/02/06 职场文书
超市活动计划书
2014/04/24 职场文书
优秀护士先进事迹
2014/05/08 职场文书
教师遵守党的政治纪律情况对照检查材料
2014/09/26 职场文书
浅析MongoDB之安全认证
2021/06/26 MongoDB
Java Kafka 消费积压监控的示例代码
2021/07/01 Java/Android
Python编程中Python与GIL互斥锁关系作用分析
2021/09/15 Python
Vue router配置与使用分析讲解
2022/12/24 Vue.js