iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >关于asyncio知识(一)
  • 717
分享到

关于asyncio知识(一)

知识asyncio 2023-01-30 23:01:52 717人浏览 独家记忆

Python 官方文档:入门教程 => 点击学习

摘要

一、介绍 asyncio 是python3.4 引入的一个新的并发模块,主要通过使用coroutines 和 futures 来让我们更容易的去实现异步的功能,并且几乎和写同步代码一样的写代码,还没有烦人的回调。 在2018年6月 3.7

一、介绍

asynciopython3.4 引入的一个新的并发模块,主要通过使用coroutines 和 futures 来让我们更容易的去实现异步的功能,并且几乎和写同步代码一样的写代码,还没有烦人的回调。

在2018年6月 3.7的更新中针对asyncio的api进行了一些升级,主要是关于task的管理以及 event loops 方面。后面会把3.7的增加的新特性专门整理一篇文章。

现状:
其实目前来说asyncio相关的异步库并不完善,官网也并没有专门维护,在GitHub上有一个俄罗斯的小组在开发维护一些常用的库如:aioMysql, aiopika, aioRedis等。 这里有一点需要在这里提前说明:如果目前想要用asyncio异步的功能,那么你整个代码中其他的库也要是异步的而不能是阻塞的,如果我们需要用aiomysql 而不能用pymysql, 我们需要用aioHttp 而不能使用requests等等等。如果恰巧你用的一些库,现在并没有相对应的异步库,那么可能就比较麻烦了。

二、Threads, loops, coroutines and futures

1. event loop:主要负责管理和分发不同task的执行,我们可以将不同的任务注册在event loop上。
2. coroutines: 我们通常也称之为协程,是与python生成器类似的特殊的函数,在这个函数中通常会有一个关键字await ,当coroutine执行到await 的时候,就会将控制权释放给event loop. 如果一个coroutine被包装成一个Future类型的Task中,那么这个coroutine就需要被event loop 去调度执行
3. futures:代表将来执行或没有执行的任务的结果,当然这个结果可能是一个异常

三、同步VS异步

asyncio 允许我们将子任务定义为coroutine,并允许你来调度它们,而在多线程中,这个调度通常是交给操作系统控制我们并不能控制。我们先通过下面的一个例子理解:

import asyncio
async def foo():
    print("running in foo")
    await asyncio.sleep(0)
    print("back foo")
async def bar():
    print("running in bar")
    await asyncio.sleep(0)
    print("back bar")
async def main():
    tasks = [foo(), bar()]
    await asyncio.gather(*tasks)
asyncio.run(main())

 

上述代码的运行结果如下:

running in foo
running in bar
back foo
back bar

 

针对上述代码的一个说明:

  1. 切记这里的sleep只能用asyncio里面的,不能直接用sleep。这里我们看到coroutine通过await的方式将控制权交还给了event loop,并切换到计划执行的下一个任务
  2. 关于gather的使用这里可以暂时忽略,后面文章会详细说明
  3. 最后使用的asyncio.run是3.7更新的新方法,负责创建一个事件循环并调度coroutine,在3.7之前是需要我们手动创建loop:asyncio.new_event_loop()

当我们的代码是同步执行的时候,执行的顺序是线性的,如果我们是异步的,顺序就变得不确定了,我们通过一个简单的爬虫的例子来理解:

import time
import random
import asyncio
import aiohttp
URL = 'https://baidu.com'
MAX_CLIENTS = 3
async def aiohttp_get(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return response
async def fetch_async(pid):
    start = time.time()
    sleepy_time = random.randint(2, 5)
    print('fetch coroutine {} started, sleeping for {} seconds'.fORMat(
        pid, sleepy_time))
    response = await aiohttp_get(URL)
    datetime = response.headers.get('Date')
    #  这里增加的asyncio.sleep是为了模拟每个请求有一定延迟返回
    await asyncio.sleep(sleepy_time)
    response.close()
    return 'coroutine {}: {}, took: {:.2f} seconds'.format(
        pid, datetime, time.time() - start)
async def main():
    start = time.time()
    futures = [fetch_async(i) for i in range(1, MAX_CLIENTS + 1)]
    for i, future in enumerate(asyncio.as_completed(futures)):
        result = await future
        print('{} {}'.format(">>" * (i + 1), result))
    print("all took: {:.2f} seconds".format(time.time() - start))
asyncio.run(main())

上述代码中,我们在每个请求里都添加了asyncio.sleep的操作,这里其实是为了模拟实际情况中当我们请求多个网站的时候,因为网络和目标网站的不同,请求返回的时间一般不同。
运行结果如下:

fetch coroutine 2 started, sleeping for 5 seconds
fetch coroutine 1 started, sleeping for 3 seconds
fetch coroutine 3 started, sleeping for 4 seconds
>> coroutine 1: Wed, 27 Feb 2019 11:27:58 GMT, took: 3.09 seconds
>>>> coroutine 3: Wed, 27 Feb 2019 11:27:58 GMT, took: 4.08 seconds
>>>>>> coroutine 2: Wed, 27 Feb 2019 11:27:58 GMT, took: 5.12 seconds
all took: 5.12 seconds

关于return_when参数

这个参数是当我们执行多个任务的时候,我只关注最快返回结果的那个任务,用法例子如下(注意我这里为了让复现一个错误,先用了Python3.7之前创建loop的方法):

import time
import random
import asyncio
import aiohttp
from concurrent.futures import FIRST_COMPLETED
URL = 'https://baidu.com'
MAX_CLIENTS = 3
async def aiohttp_get(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return response
async def fetch_async(pid):
    start = time.time()
    sleepy_time = random.randint(2, 5)
    print('fetch coroutine {} started, sleeping for {} seconds'.format(
        pid, sleepy_time))
    response = await aiohttp_get(URL)
    datetime = response.headers.get('Date')
    #  这里增加的asyncio.sleep是为了模拟每个请求有一定延迟返回
    await asyncio.sleep(sleepy_time)
    response.close()
    return 'coroutine {}: {}, took: {:.2f} seconds'.format(
        pid, datetime, time.time() - start)
async def main():
    start = time.time()
    futures = [fetch_async(i) for i in range(1, MAX_CLIENTS + 1)]
    done, pending = await asyncio.wait(
        futures, return_when=FIRST_COMPLETED
    )
    print(done.pop().result())
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()

运行结果会出现如下情况:

fetch coroutine 2 started, sleeping for 2 seconds
fetch coroutine 1 started, sleeping for 5 seconds
fetch coroutine 3 started, sleeping for 2 seconds
coroutine 2: Wed, 27 Feb 2019 11:41:19 GMT, took: 2.11 seconds
Task was destroyed but it is pending!
task: <Task pending coro=<fetch_async() done, defined at e:/vs_python/lean_asyncio/ex2.py:17> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x00000000038E5798>()]>>

其实这里出现这种问题的原因,我们很容易理解,我们开启了三个任务,当我们收到最快完成的那个之后就关闭了循环,后面的两个任务还处于pending状态,asyncio 认为这是一个错误,所以打印出了我们看到的那个警告:Task was destroyed but it is pending!
我们如何解决这个问题呢?

四、关于future

future有四种状态:

  1. Pending
  2. Running
  3. Done
  4. Cancelled

我们可以通过调用done, cancelled 或者 running 来看当前future是否处于该状态,这里再次提醒,done 状态可以表示返回结果,也可以表示跑出了异常。我们也可以通过调用cancel来专门取消future,不过在python3.7之后,asyncio.run替我们做了这些事情,我们把上面的那个出现Task was destroyed but it is pending!的代码进行更改:

import time
import random
import asyncio
import aiohttp
from concurrent.futures import FIRST_COMPLETED
URL = 'https://baidu.com'
MAX_CLIENTS = 3
async def aiohttp_get(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return response
async def fetch_async(pid):
    start = time.time()
    sleepy_time = random.randint(2, 5)
    print('fetch coroutine {} started, sleeping for {} seconds'.format(
        pid, sleepy_time))
    response = await aiohttp_get(URL)
    datetime = response.headers.get('Date')
    #  这里增加的asyncio.sleep是为了模拟每个请求有一定延迟返回
    await asyncio.sleep(sleepy_time)
    response.close()
    return 'coroutine {}: {}, took: {:.2f} seconds'.format(
        pid, datetime, time.time() - start)
async def main():
    start = time.time()
    futures = [fetch_async(i) for i in range(1, MAX_CLIENTS + 1)]
    done, pending = await asyncio.wait(
        futures, return_when=FIRST_COMPLETED
    )
    print(done.pop().result())
asyncio.run(main())

运行结果如下,完全正常了:

fetch coroutine 2 started, sleeping for 5 seconds
fetch coroutine 3 started, sleeping for 2 seconds
fetch coroutine 1 started, sleeping for 2 seconds
coroutine 3: Wed, 27 Feb 2019 11:54:13 GMT, took: 2.07 seconds

future还有一个实用的功能:允许我们在future变成完成状态时添加callback回调.

关于future的完成时结果的获取,通过下面代码来演示:

import time
import random
import asyncio
import aiohttp
from concurrent.futures import FIRST_COMPLETED
URL = 'https://httpbin.org/get'
MAX_CLIENTS = 3
async def aiohttp_get(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return response
async def fetch_async(pid):
    start = time.time()
    sleepy_time = random.randint(2, 5)
    print('fetch coroutine {} started, sleeping for {} seconds'.format(
        pid, sleepy_time))
    response = await aiohttp_get(URL)
    datetime = response.headers.get('Date')
    #  这里增加的asyncio.sleep是为了模拟每个请求有一定延迟返回
    await asyncio.sleep(sleepy_time)
    response.close()
    return 'coroutine {}: {}, took: {:.2f} seconds'.format(
        pid, datetime, time.time() - start)
async def main():
    start = time.time()
    futures = [fetch_async(i) for i in range(1, MAX_CLIENTS + 1)]
    done, pending = await asyncio.wait(
        futures
    )
    print(done)
    for future in done:
        print(future.result())
asyncio.run(main())

运行结果如下:

fetch coroutine 2 started, sleeping for 5 seconds
fetch coroutine 1 started, sleeping for 2 seconds
fetch coroutine 3 started, sleeping for 4 seconds
{<Task finished coro=<fetch_async() done, defined at e:/vs_python/lean_asyncio/ex2.py:17> result='coroutine 3:... 5.31 seconds'>, <Task finished coro=<fetch_async() done, defined at e:/vs_python/lean_asyncio/ex2.py:17> result='coroutine 1:... 3.34 seconds'>, <Task finished coro=<fetch_async() done, defined at e:/vs_python/lean_asyncio/ex2.py:17> result='coroutine 2:... 6.38 seconds'>}
coroutine 3: Wed, 27 Feb 2019 12:10:15 GMT, took: 5.31 seconds
coroutine 1: Wed, 27 Feb 2019 12:10:15 GMT, took: 3.34 seconds
coroutine 2: Wed, 27 Feb 2019 12:10:15 GMT, took: 6.38 seconds

我们可以看到,当所有任务完成后,我们可以通过done获取每个人的结果信息。

我们也可以给我们的任务添加超时时间

import time
import random
import asyncio
import aiohttp
from concurrent.futures import FIRST_COMPLETED
URL = 'https://httpbin.org/get'
MAX_CLIENTS = 3
async def aiohttp_get(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return response
async def fetch_async(pid):
    start = time.time()
    sleepy_time = random.randint(2, 5)
    print('fetch coroutine {} started, sleeping for {} seconds'.format(
        pid, sleepy_time))
    response = await aiohttp_get(URL)
    datetime = response.headers.get('Date')
    #  这里增加的asyncio.sleep是为了模拟每个请求有一定延迟返回
    await asyncio.sleep(sleepy_time)
    response.close()
    return 'coroutine {}: {}, took: {:.2f} seconds'.format(
        pid, datetime, time.time() - start)
async def main():
    start = time.time()
    futures = [fetch_async(i) for i in range(1, MAX_CLIENTS + 1)]
    done, pending = await asyncio.wait(
        futures, return_when=FIRST_COMPLETED,timeout=0.01
    )
    print(done)
    for future in done:
        print(future.result())
asyncio.run(main())

我这里把超时时间设置的非常小了是0.01,导致最后我打印done结果的时候其实三个任务没有一任务是被完成的:

fetch coroutine 2 started, sleeping for 4 seconds
fetch coroutine 3 started, sleeping for 3 seconds
fetch coroutine 1 started, sleeping for 4 seconds
set()

五、总结

这里对python asyncio先进行整体功能的整理,会面会针对细节做详细整理。相对来说现在各个公司实际线上用asyncio的应该不多,也希望更多的小伙伴来相互交流,分享这个python以及python异步相关心得。欢迎加入交流群:948510543

 

--结束END--

本文标题: 关于asyncio知识(一)

本文链接: https://www.lsjlt.com/news/181242.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 关于asyncio知识(一)
    一、介绍 asyncio 是python3.4 引入的一个新的并发模块,主要通过使用coroutines 和 futures 来让我们更容易的去实现异步的功能,并且几乎和写同步代码一样的写代码,还没有烦人的回调。 在2018年6月 3.7...
    99+
    2023-01-30
    知识 asyncio
  • 关于umask(权限掩码)的一些知识
    关于umask(权限掩码)的一些知识 前言:首先,我们先介绍一下关于umask的一些基础知识,以方便更深入的了解umask 定义: umask(user-file-creation mode mask...
    99+
    2023-09-30
    linux 服务器 运维
  • 关于python列表相关知识点
    目录python列表1.列表的创建与删除列表的特点1.列表元素按顺序有序排序2.索引映射唯一数据3.列表可以存储重复数据4.任意数据类型混存5.根据需要动态分配和回收内存2.列表的查...
    99+
    2023-05-16
    python python列表
  • 关于JavaScript相关知识有哪些
    这期内容当中小编将会给大家带来有关关于JavaScript相关知识有哪些,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。  (一)JS中基本类型和引用类型  JavaScr...
    99+
    2024-04-02
  • 关于前端要知道的 AST知识
    目录认识 ASTestree[1]认识 acorn[2]基本操作实现普通函数转换为箭头函数认识 AST 定义:在计算机科学中,抽象语法树是源代码语法结构的一种抽象表示。它以...
    99+
    2023-05-15
    前端 前端AST知识
  • Javascript基础知识中关于内置对象的知识
    目录1、内置对象介绍1.1Math对象1.2Math中的方法1.3Date对象2、Date中的方法3、经典案例:倒计时效果:4、Array数组对象4.1数组的创建4.2数组中的常用方...
    99+
    2024-04-02
  • Python协程及asyncio基础知识有哪些
    小编给大家分享一下Python协程及asyncio基础知识有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Python协程及asyncio基础知识协程(cor...
    99+
    2023-06-15
  • 关于Java8的知识点有哪些
    这篇文章主要讲解了“关于Java8的知识点有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“关于Java8的知识点有哪些”吧!在了解一项新技术之前,我们需要了解我们为什么要去学习它以及它的...
    99+
    2023-06-16
  • 关于useState的知识点有哪些
    本篇内容介绍了“关于useState的知识点有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!hook如...
    99+
    2024-04-02
  • mysql关于variable知识点有哪些
    这篇文章主要讲解了“mysql关于variable知识点有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“mysql关于variable知识点有哪些”吧!...
    99+
    2024-04-02
  • 有哪些关于TypeScript的知识点
    这篇文章主要介绍“有哪些关于TypeScript的知识点”,在日常操作中,相信很多人在有哪些关于TypeScript的知识点问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”有哪...
    99+
    2024-04-02
  • 有关python的一些小知识
    1.关于input:input()为标准输入函数,和C语言中scanf的用法类似。但在使用input时,会从标准输入中读取一个string(字符串),对于用户换行不会读入。如果想转换为整型,a=int(input(“请输入:”))。2.fo...
    99+
    2023-01-31
    小知识 python
  • 关于链表的知识点有哪些
    本篇内容介绍了“关于链表的知识点有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一 单向链表1.1 &...
    99+
    2024-04-02
  • 关于Java IO的知识点有哪些
    本篇内容主要讲解“关于Java IO的知识点有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“关于Java IO的知识点有哪些”吧!传统的 BIOJava IO流是一个庞大的生态环境,其内部提...
    99+
    2023-06-16
  • 关于主键的知识点有哪些
    本篇内容介绍了“关于主键的知识点有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1. UUID模式通用...
    99+
    2024-04-02
  • 关于Nginx中虚拟主机的一些冷门知识小结
    目录前言对线上配置的一个小疑问问题背景实测结果探索排查网络排查nginx总结前言 nginx的虚拟主机,不知道大家了解不。以前吧,如果在nginx上要反向代理多个服务,我一般是让ng...
    99+
    2024-04-02
  • 详细聊聊关于sql注入的一些零散知识点
    目录零、本文涉及知识点一、sqlmap写一句马的过程(-- os-shell)1.1 简述过程1.2 一个小问题二、堆叠注入:2.1 什么是堆叠注入2.2 如何判断存在堆叠注入?2....
    99+
    2024-04-02
  • mongodb中关于索引的知识有哪些
    这篇文章主要为大家展示了“mongodb中关于索引的知识有哪些”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“mongodb中关于索引的知识有哪些”这篇文章吧。我...
    99+
    2024-04-02
  • 关于包导入的知识点有哪些
    本篇内容主要讲解“关于包导入的知识点有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“关于包导入的知识点有哪些”吧! 1. 使用 __all__ 控...
    99+
    2024-04-02
  • R语言关于“包”的知识点总结
    R语言的包是R函数,编译代码和样本数据的集合。 它们存储在R语言环境中名为“library”的目录下。 默认情况下,R语言在安装期间安装一组软件包。 随后添加更多包,当它们用于某些特...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作