浅谈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 12 Python
Python列表推导式、字典推导式与集合推导式用法实例分析
Feb 07 Python
解决python字典对值(值为列表)赋值出现重复的问题
Jan 20 Python
在Python中如何传递任意数量的实参的示例代码
Mar 21 Python
Python3.5装饰器原理及应用实例详解
Apr 30 Python
django中账号密码验证登陆功能的实现方法
Jul 15 Python
浅谈python3中input输入的使用
Aug 02 Python
opencv python如何实现图像二值化
Feb 03 Python
Python Flask上下文管理机制实例解析
Mar 16 Python
jupyter lab文件导出/下载方式
Apr 22 Python
tensorflow 20:搭网络,导出模型,运行模型的实例
May 26 Python
matplotlib如何设置坐标轴刻度的个数及标签的方法总结
Jun 11 Python
Python3接口性能测试实例代码
Jun 20 #Python
使用Djongo模块在Django中使用MongoDB数据库
python自动计算图像数据集的RGB均值
详解如何用Python实现感知器算法
python中24小时制转换为12小时制的方法
Jun 18 #Python
用Python selenium实现淘宝抢单机器人
python中pandas对多列进行分组统计的实现
You might like
SONY ICF-SW7600的电路分析
2021/03/02 无线电
PHP 高手之路(三)
2006/10/09 PHP
PHP Google的translate API代码
2008/12/10 PHP
PHP json_decode函数详细解析
2014/02/17 PHP
Java和PHP在Web开发方面对比分析
2015/03/01 PHP
PHP获取文件扩展名的常用方法小结【五种方式】
2018/04/27 PHP
Laravel中GraphQL接口请求频率实战记录
2020/09/01 PHP
可恶的ie8提示缺少id未定义
2014/03/20 Javascript
sogou地图API用法实例教程
2014/09/11 Javascript
jQuery中Ajax的load方法详解
2015/01/14 Javascript
JavaScript面向对象的实现方法小结
2015/04/14 Javascript
javascript中setAttribute()函数使用方法及兼容性
2015/07/19 Javascript
JavaScript简单遍历DOM对象所有属性的实现方法
2015/10/21 Javascript
SpringMVC restful 注解之@RequestBody进行json与object转换
2015/12/10 Javascript
探讨AngularJs中ui.route的简单应用
2016/11/16 Javascript
Angular2.js实现表单验证详解
2017/06/23 Javascript
基于Vue.js 2.0实现百度搜索框效果
2020/12/28 Javascript
Three.js入门之hello world以及如何绘制线
2017/09/25 Javascript
vue组件通信传值操作示例
2019/01/08 Javascript
微信小程序sessionid不一致问题解决
2019/08/30 Javascript
微信小程序开发打开另一个小程序的实现方法
2020/05/17 Javascript
详解vue-router的导航钩子(导航守卫)
2020/11/02 Javascript
Python生成随机MAC地址
2015/03/10 Python
Python实现简单生成验证码功能【基于random模块】
2018/02/10 Python
Python ORM框架Peewee用法详解
2020/04/29 Python
耐克奥地利官网:Nike奥地利
2019/08/16 全球购物
教师实习自我鉴定
2013/12/18 职场文书
工作自我评价怎么写
2014/01/29 职场文书
超市活动计划书
2014/04/24 职场文书
党的群众路线教育实践活动总结大会主持词
2014/10/30 职场文书
大学生旷课检讨书1000字
2015/02/19 职场文书
工会经费申请报告
2015/05/15 职场文书
用Python selenium实现淘宝抢单机器人
2021/06/18 Python
python画条形图的具体代码
2022/04/20 Python
Go调用Rust方法及外部函数接口前置
2022/06/14 Golang
python数字图像处理之图像的批量处理
2022/06/28 Python