浅谈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 相关文章推荐
Tensorflow 实现修改张量特定元素的值方法
Jul 30 Python
python判断一个数是否能被另一个整数整除的实例
Dec 12 Python
Python中出现IndentationError:unindent does not match any outer indentation level错误的解决方法
Apr 18 Python
python 搭建简单的http server,可直接post文件的实例
Jan 03 Python
Pyqt清空某一个QTreeewidgetItem下的所有分支方法
Jun 17 Python
python字典改变value值方法总结
Jun 21 Python
Python依赖包整体迁移方法详解
Aug 15 Python
SpringBoot实现登录注册常见问题解决方案
Mar 04 Python
Python CSS选择器爬取京东网商品信息过程解析
Jun 01 Python
从python读取sql的实例方法
Jul 21 Python
Python self用法详解
Nov 28 Python
Python入门学习之类的相关知识总结
May 25 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/04 星际争霸
PHP易混淆知识整理笔记
2015/09/24 PHP
thinkPHP模型初始化实例分析
2015/12/03 PHP
从性能方面考虑PHP下载远程文件的3种方法
2015/12/29 PHP
javascript OFFICE控件测试代码
2009/12/08 Javascript
jQuery html() in Firefox (uses .innerHTML) ignores DOM changes
2010/03/05 Javascript
利用javascript的面向对象的特性实现限制试用期
2011/08/04 Javascript
js动态添加删除,后台取数据(示例代码)
2013/11/25 Javascript
js数组依据下标删除元素
2015/04/14 Javascript
JS中取二维数组中最大值的方法汇总
2016/04/17 Javascript
Javascript实现动态时钟效果
2018/11/17 Javascript
使用vue-router切换页面时,获取上一页url以及当前页面url的方法
2019/05/06 Javascript
Vue form表单动态添加组件实战案例
2019/09/02 Javascript
JS常用排序方法实例代码解析
2020/03/03 Javascript
[02:28]DOTA2亚洲邀请赛附加赛 RECAP赛事回顾
2015/01/29 DOTA
[39:07]LGD vs VP 2018国际邀请赛淘汰赛BO3 第二场 8.21
2018/08/22 DOTA
Python入门_学会创建并调用函数的方法
2017/05/16 Python
Python 异常处理的实例详解
2017/09/11 Python
python获取指定字符串中重复模式最高的字符串方法
2018/06/29 Python
python截取两个单词之间的内容方法
2018/12/25 Python
Django上线部署之IIS的配置方法
2019/08/22 Python
Django后端发送小程序微信模板消息示例(服务通知)
2019/12/17 Python
爬虫代理池Python3WebSpider源代码测试过程解析
2019/12/20 Python
Python生成随机验证码代码实例解析
2020/06/09 Python
Python识别处理照片中的条形码
2020/11/16 Python
css3中检验表单的required,focus,valid和invalid样式
2014/02/21 HTML / CSS
Lookfantastic澳大利亚官网:英国知名美妆购物网站
2021/01/07 全球购物
自荐信如何“自荐”
2013/10/24 职场文书
学校办公室主任职责
2013/12/27 职场文书
残疾人创业典型事迹
2014/02/01 职场文书
市场营销大学生职业规划书
2014/02/25 职场文书
代理协议书范本
2014/04/22 职场文书
新党章的学习心得体会
2014/11/07 职场文书
党校个人总结
2015/03/04 职场文书
创业计划书之餐饮馄饨店
2019/07/18 职场文书
《初涉尘世》读后感3篇
2020/01/10 职场文书