python asyncio 协程异步【1】详解(1)

简介: python asyncio 协程异步【1】详解(1)

python 模块 (1)asyncio 协程异步

文章目录

python 模块 (1)asyncio 协程异步

1. asyncio介绍

2. 协程基础

3. 让协程返回值以及yield from说明

4. yield from 说明

5. 异步IO(asyncio)原理

6. asyncio API基础

7. async 函数的示例

8. asyncio三种执行协程的机制:

9. “可等待”对象(Awaitables)

10. Coroutine(协程)

11. Task(任务)

12. Future(未来对象)

13. 执行asyncio程序

1. asyncio介绍

异步IO:就是发起一个IO操作(如:网络请求,文件读写等),这些操作一般是比较耗时的,不用等待它结束,可以继续做其他事情,结束时会发来通知。


协程:又称为微线程,在一个线程中执行,执行函数时可以随时中断,由程序(用户)自身控制,执行效率极高,与多线程比较,没有切换线程的开销和多线程锁机制。


python中异步IO操作是通过asyncio来实现的。

2. 协程基础

从语句上看,协程和生成器类似,都是包含了yield关键字,不同之处在于协程中yield关键词通常出现在=右边,可以产出值a(y = yield a)或不产出值时为None(y = yield)。调用方可以用send函数发送值给协程。


激活协程时在yield处暂停,等待调用方发送数据,下次继续在yield暂停。从根本上看,yield是流程控制的工具,可以实现协作式多任务,这也是后面讲解异步IO的基础。

示例1:

#!/usr/bin/python
def coroutine_example(name):
    print('start coroutine...name:', name)
    x = yield name #调用next()时,产出yield右边的值后暂停;调用send()时,产出值赋给x,并往下运行
    print('send值:', x)
if __name__ == '__main__':
  coro = coroutine_example('Zarten')
  print('next的返回值:', next(coro))
  print('send的返回值:', coro.send(6))

输出:

$ python3 ns1.py
start coroutine...name: Zarten
next的返回值: Zarten
send值: 6
Traceback (most recent call last):
  File "ns1.py", line 11, in <module>
    print('send的返回值:', coro.send(6))
StopIteration

必须先调用next()函数预激活协程,不然send()函数无法使用。


调用next()时,产出yield右边的值后暂停不再往yield的下一行执行(一般不需要next产出值),等待send的到来,调用send()时,产出值赋给x(可对x作进一步处理),并往下运行。


协程结束时会跟生成器一样抛出StopIteration的异常给调用方,调用方可以捕获它后处理。

3. 让协程返回值以及yield from说明

获取协程的返回值

当结束协程时,会返回返回值,调用方会抛出StopIteration异常,返回值就在异常对象的value属性中。

#!/usr/bin/python
def coroutine_example(name):
    print('start coroutine...name:', name)
    while True:
        x = yield name #调用next()时,产出yield右边的值后暂停;调用send()时,产出值赋给x,并往下运行
        if x is None:
            return 'zhihuID: Zarten'
        print('send值:', x)
coro = coroutine_example('Zarten')
next(coro)
print('send的返回值:', coro.send(6))
try:
    coro.send(None)
except StopIteration as e:
    print('返回值:', e.value)

4. yield from 说明

yield from跟for循环很相似,但功能更多一些

#!/usr/bin/python
def for_test():
    for i in range(3):
       yield i
print(list(for_test()))
def yield_from_test():
    yield from range(3)
print(list(yield_from_test()))

输出:

$ python3 ns3.py
[0, 1, 2]
[0, 1, 2]

其实yield from内部会自动捕获StopIteration异常,并把异常对象的value属性变成yield from表达式的值。


yield from x 表达式内部首先是调用iter(x),然后再调用next(),因此x是任何的可迭代对象。yield from 的主要功能就是打开双向通道,把最外层的调用方和最内层的子生成器连接起来。


下面代码展示:调用方发送的值在yield from表达式处直接传递给子生成器,并在yield from处等待子生成器的返回。

#!/usr/bin/python
def coroutine_example(name):
    print('start coroutine...name:', name)
    x = yield name #调用next()时,产出yield右边的值后暂停;调用send()时,产出值赋给x,并往下运行
    print('send值:', x)
    return 'zhihuID: Zarten'
def grouper2():
    result2 = yield from coroutine_example('Zarten') #在此处暂停,等待子生成器的返回后继续往下执行
    print('result2的值:', result2)
    return result2
def grouper():
    result = yield from grouper2() #在此处暂停,等待子生成器的返回后继续往下执行
    print('result的值:', result)
    return result
def main():
    g = grouper()
    next(g)
    try:
        g.send(10)
    except StopIteration as e:
        print('返回值:', e.value)
if __name__ == '__main__':
    main()
$ python3 ns4.py 
start coroutine...name: Zarten
send值: 10
result2的值: zhihuID: Zarten
result的值: zhihuID: Zarten
返回值: zhihuID: Zarten

从上面也可看到yield from起到一个双向通道的作用,同时子生成器也可使用yield from调用另一个子生成器,一直嵌套下去直到遇到yield表达式结束链式。

yield from一般用于asyncio模块做异步IO

5. 异步IO(asyncio)原理

asyncio 模块最大特点就是,只存在一个线程,跟 JavaScript 一样。


由于只有一个线程,就不可能多个任务同时运行。asyncio 是"多任务合作"模式(cooperative multitasking),允许异步任务交出执行权给其他任务,等到其他任务完成,再收回执行权继续往下执行,这跟 JavaScript 也是一样的。


由于代码的执行权在多个任务之间交换,所以看上去好像多个任务同时运行,其实底层只有一个线程,多个任务分享运行时间。表面上,这是一个不合理的设计,明明有多线程多进程的能力,为什么放着多余的 CPU 核心不用,而只用一个线程呢?但是就像前面说的,单线程简化了很多问题,使得代码逻辑变得简单,写法符合直觉。


syncio 模块在单线程上启动一个事件循环(event loop),时刻监听新进入循环的事件,加以处理,并不断重复这个过程,直到异步任务结束。事件循环的内部机制,可以参考 JavaScript 的模型,两者是一样的。

1832b220aa754cd18c504acc7686a560.png

6. asyncio API基础

下面介绍 asyncio 模块最主要的几个API。注意,必须使用 Python 3.7 或更高版本,早期的语法已经变了。

第一步,import 加载 asyncio 模块。

import asyncio

第二步,函数前面加上 async 关键字,就变成了 async 函数。这种函数最大特点是执行可以暂停,交出执行权。

async def main():

第三步,在 async 函数内部的异步任务前面,加上await命令。

await asyncio.sleep(1)

上面代码中,asyncio.sleep(1) 方法可以生成一个异步任务,休眠1秒钟然后结束。

执行引擎遇到await命令,就会在异步任务开始执行之后,暂停当前 async 函数的执行,把执行权交给其他任务。等到异步任务结束,再把执行权交回 async 函数,继续往下执行。

第四步,async.run() 方法加载 async 函数,启动事件循环。

asyncio.run(main())

上面代码中,asyncio.run() 在事件循环上监听 async 函数main的执行。等到 main 执行完了,事件循环才会终止。

7. async 函数的示例

#!/usr/bin/env python3
# async.py
import asyncio
async def count():
    print("One")
    await asyncio.sleep(1)
    print("Two")
async def main():
    await asyncio.gather(count(), count(), count())
asyncio.run(main())

输出:

$ python3 async.py
One
One
One
Two
Two
Two

在 async 函数main的里面,asyncio.gather() 方法将多个异步任务(三个 count())包装成一个新的异步任务,必须等到内部的多个异步任务都执行结束,这个新的异步任务才会结束。


三个 count() 依次执行,打印完 One,就休眠1秒钟,把执行权交给下一个 count(),所以先连续打印出三个 One。等到1秒钟休眠结束,执行权重新交回第一个 count(),开始执行 await 命令下一行的语句,所以会接着打印出三个Two。脚本总的运行时间是1秒。


作为对比,下面是这个例子的同步版本 sync.py。

#!/usr/bin/env python3
# sync.py
import time
def count():
    print("One")
    time.sleep(1)
    print("Two")
def main():
    for _ in range(3):
        count()
main()

输出:

$ python3 sync.py 
One
Two
One
Two
One
Two
相关文章
|
1月前
|
Python
Python中的异步编程:使用asyncio和aiohttp实现高效网络请求
【10月更文挑战第34天】在Python的世界里,异步编程是提高效率的利器。本文将带你了解如何使用asyncio和aiohttp库来编写高效的网络请求代码。我们将通过一个简单的示例来展示如何利用这些工具来并发地处理多个网络请求,从而提高程序的整体性能。准备好让你的Python代码飞起来吧!
82 2
|
1月前
|
调度 开发者 Python
Python中的异步编程:理解asyncio库
在Python的世界里,异步编程是一种高效处理I/O密集型任务的方法。本文将深入探讨Python的asyncio库,它是实现异步编程的核心。我们将从asyncio的基本概念出发,逐步解析事件循环、协程、任务和期货的概念,并通过实例展示如何使用asyncio来编写异步代码。不同于传统的同步编程,异步编程能够让程序在等待I/O操作完成时释放资源去处理其他任务,从而提高程序的整体效率和响应速度。
|
1月前
|
API 调度 开发者
探索Python中的异步编程:从asyncio到Trio
本文将带你深入Python异步编程的心脏地带,从asyncio的基本概念到Trio的高级特性,我们将一起揭开Python异步编程的神秘面纱,并探讨它们如何改变我们的编程方式。
|
27天前
|
数据采集 JSON 测试技术
Grequests,非常 Nice 的 Python 异步 HTTP 请求神器
在Python开发中,处理HTTP请求至关重要。`grequests`库基于`requests`,支持异步请求,通过`gevent`实现并发,提高性能。本文介绍了`grequests`的安装、基本与高级功能,如GET/POST请求、并发控制等,并探讨其在实际项目中的应用。
37 3
|
1月前
|
API 开发者 Python
探索Python中的异步编程:Asyncio与Tornado的对决
在这个快节奏的世界里,Python开发者面临着一个挑战:如何让代码跑得更快?本文将带你走进Python异步编程的两大阵营——Asyncio和Tornado,探讨它们如何帮助我们提升性能,以及在实际应用中如何选择。我们将通过一场虚拟的“对决”,比较这两个框架的性能和易用性,让你在异步编程的战场上做出明智的选择。
|
1月前
|
数据库 Python
异步编程不再难!Python asyncio库实战,让你的代码流畅如丝!
在编程中,随着应用复杂度的提升,对并发和异步处理的需求日益增长。Python的asyncio库通过async和await关键字,简化了异步编程,使其变得流畅高效。本文将通过实战示例,介绍异步编程的基本概念、如何使用asyncio编写异步代码以及处理多个异步任务的方法,帮助你掌握异步编程技巧,提高代码性能。
87 4
|
1月前
|
API 数据处理 Python
探秘Python并发新世界:asyncio库,让你的代码并发更优雅!
在Python编程中,随着网络应用和数据处理需求的增长,并发编程变得愈发重要。asyncio库作为Python 3.4及以上版本的标准库,以其简洁的API和强大的异步编程能力,成为提升性能和优化资源利用的关键工具。本文介绍了asyncio的基本概念、异步函数的定义与使用、并发控制和资源管理等核心功能,通过具体示例展示了如何高效地编写并发代码。
43 2
|
1月前
|
测试技术 Python
Python中的异步编程与`asyncio`库
Python中的异步编程与`asyncio`库
|
1月前
|
调度 开发者 Python
异步编程在Python中的应用:Asyncio和Coroutines
异步编程在Python中的应用:Asyncio和Coroutines
32 1
|
1月前
|
NoSQL 关系型数据库 MySQL
python协程+异步总结!
本文介绍了Python中的协程、asyncio模块以及异步编程的相关知识。首先解释了协程的概念和实现方法,包括greenlet、yield关键字、asyncio装饰器和async/await关键字。接着详细讲解了协程的意义和应用场景,如提高IO密集型任务的性能。文章还介绍了事件循环、Task对象、Future对象等核心概念,并提供了多个实战案例,包括异步Redis、MySQL操作、FastAPI框架和异步爬虫。最后提到了uvloop作为asyncio的高性能替代方案。通过这些内容,读者可以全面了解和掌握Python中的异步编程技术。
51 0