Python asyncio

本文介绍了Python中的asyncio模块,用于实现异步编程,特别是针对IO绑定任务。asyncio提供了事件循环、协程和future等工具,通过示例展示了如何创建和运行协程,使用create_task、gather和run函数进行并发控制。此外,还提到了httpx库用于异步HTTP请求,以及MSPlaywright库在异步模式下自动化浏览器。
摘要由CSDN通过智能技术生成

在本文中,我们将展示如何在Python中使用asyncio模块进行异步编程。

通过异步编程,我们可以在执行主程序的同时执行任务。

asyncio模块

asyncio是一个用Python编写异步程序的库。

该模块提供了高级API:

  • 并发运行Python协程
  • 执行网络IO和IPC
  • 控制子过程
  • 通过队列分发任务
  • 同步并发代码

并发编程用于两种任务:IO绑定任务和CPU绑定任务。从网络请求数据、访问数据库或读写都是IO绑定的任务。CPU密集型任务是计算开销很大的任务,例如数学计算或图形处理。

注意:异步编程适用于IO绑定任务。

要在Python中进行异步编程,我们使用事件循环(event loop)协程(coroutine)期货?(future)。事件循环是主要任务,它负责管理异步任务并将它们分发给执行。协程是调度事件执行的函数。期货是协同程序执行的结果。结果可能以异常结束。

协程是用于协作多任务处理的Python函数,它们可以暂停和恢复。async关键字用于创建Python协程。await关键字挂起协程的执行,直到它完成并返回结果数据。

Python asyncio的简单示例

下面是一个简单的asyncio示例。

simple.py

#!/usr/bin/python

import asyncio

async def mul(x, y):
    return x * y

loop = asyncio.get_event_loop()

try:
    res2 = loop.run_until_complete(mul(5, 5))
    print(res2)

finally:
    loop.close()

该程序创建并运行一个异步函数,该函数将两个数字相乘。

async def mul(x, y):
    return x * y

协程是用async修饰符声明的函数。

loop = asyncio.get_event_loop()

get_event_loop返回一个asyncio事件循环。执行异步代码需要一个事件循环。

res = loop.run_until_complete(mul(5, 5))

run_until_complete函数运行事件循环,直到future完成。它返回future的结果,或者引发异常。future表示异步操作的最终结果。

Python asyncio create_task

create_task函数将给定的协程包装到一个任务中,并安排其执行。它返回任务对象。任务在get_event_loop返回的循环中执行。

协程被包装到任务中以获得额外的功能,例如任务取消或检查就绪状态。

create_task.py

#!/usr/bin/python

import asyncio

async def mul(x, y):
    return x * y

loop = asyncio.get_event_loop()

try:
    task = loop.create_task(mul(10, 10))

    res = loop.run_until_complete(task)
    print(res)

finally:
    loop.close()

该示例创建一个任务并安排其执行。它打印最终结果。

asyncioget_running_loopget_event_loop的区别
get_running_loop() 是python3.7之后新增的函数,用于获取当前正在运行的event loop,如果当前主线程中没有正在运行的event loop,就会报RuntimeError 错误。
并且get_running_loop 要在协程里面用,用来捕获当前的loop。
参考这篇文章以及另一篇文章

Python asyncio.sleep

asyncio.sleep函数在给定的秒数内休眠当前协同程序。该函数通常用于模拟长时间运行的任务。

sleep.py

#!/usr/bin/python

import asyncio
import time

async def task(tid, n):

    await asyncio.sleep(n)
    print(f'task {tid} finished')

loop = asyncio.get_event_loop()

tm1 = time.perf_counter()

try:
    t1 = loop.create_task(task(1, 3))
    loop.run_until_complete(t1)

    t2 = loop.create_task(task(2, 2))
    loop.run_until_complete(t2)

    t3 = loop.create_task(task(3, 1))
    loop.run_until_complete(t3)

finally:
    loop.close()

tm2 = time.perf_counter()
print(f'Total time elapsed: {tm2-tm1:0.2f} seconds')

在本例中,我们创建并调度了三个任务。这些任务模拟asyncio.sleep的一些工作。每个任务与主程序异步运行,但任务本身在主程序中顺序运行。我们使用time.perf_counter测量经过的时间。

$ ./sleep.py
task 1 finished
task 2 finished
task 3 finished
Total time elapsed: 6.01 seconds

Python asyncio gather

asyncio.gather函数用于并发调度多个协程。

gather.py

#!/usr/bin/python

import asyncio
import time

async def task(tid, n):

    await asyncio.sleep(n)
    print(f'task {tid} finished')

loop = asyncio.get_event_loop()

tm1 = time.perf_counter()

try:

    tasks = [
        loop.create_task(task(1, 3)),
        loop.create_task(task(2, 2)),
        loop.create_task(task(3, 1))
    ]

    loop.run_until_complete(asyncio.gather(*tasks))

finally:
    loop.close()

tm2 = time.perf_counter()
print(f'Total time elapsed: {tm2-tm1:0.2f} seconds')

在本例中,我们使用asyncio.gather并发地运行这三个任务。

$ ./asyncio_gather.py
task 3 finished
task 2 finished
task 1 finished
Total time elapsed: 3.00 seconds

Python asyncio.run

使用asyncio.run是一个方便的函数,它简化了代码。该函数创建一个事件循环,调度协程,最后关闭循环。

run.py

#!/usr/bin/python

import asyncio
import time

async def task(tid, n):

    await asyncio.sleep(n)
    print(f'task {tid} finished')


async def main():

    t1 = asyncio.create_task(task(1, 3))
    t2 = asyncio.create_task(task(2, 2))
    t3 = asyncio.create_task(task(3, 1))

    await asyncio.gather(t1, t2, t3)

tm1 = time.perf_counter()

asyncio.run(main())

tm2 = time.perf_counter()
print(f'Total time elapsed: {tm2-tm1:0.2f} seconds')

使用asyncio.run,代码更加紧凑。

Python asyncio echo server

在下面的示例中,我们创建一个回显服务器。echo服务器将来自客户端的消息发送回来。

echo_server.py

#!/usr/bin/python

import asyncio

class EchoProtocol(asyncio.Protocol):

    def connection_made(self, transport):
        self.transport = transport

    def data_received(self, data):
        print(f'received: {data}')
        self.transport.write(data)

async def main(host, port):

    print(f'starting server on port {port}')

    loop = asyncio.get_running_loop()
    server = await loop.create_server(EchoProtocol, host, port)
    await server.serve_forever()

try:
    asyncio.run(main('127.0.0.1', 8001))
except KeyboardInterrupt:
    print('terminated')

create_server函数返回一个协程,该协程创建一个绑定到主机和端口的TCP服务器。serve_forever函数开始接受连接,直到协程被取消。当协程被取消时,服务器将被关闭。

$ nc 127.0.0.1 8001
Hello!
Hello!
Hi!
Hi!

我们使用nc命令测试服务器。

多个HTTP请求

为了发出多个异步HTTP请求,我们使用httpx模块。该模块提供了一个包含同步和异步API的HTTP客户端,并支持HTTP/1.1和HTTP/2。

async_req.py

#!/usr/bin/python

import httpx
import asyncio

async def get_async(url):
    async with httpx.AsyncClient() as client:
        return await client.get(url)

urls = ['http://webcode.me', 'https://httpbin.org/get',
    'https://google.com', 'https://stackoverflow.com',
    'https://github.com']

async def launch():

    resps = await asyncio.gather(*map(get_async, urls))
    data = [resp.status_code for resp in resps]

    for status_code in data:
        print(status_code)

asyncio.run(launch())

该示例向五个网站发出异步HTTP请求。它打印所有提供的URL的状态码。

$ ./async_req.py
200
200
200
200
200

Python剧作家async的例子

许多库都支持异步编程。MS Playwright 库允许在同步和异步模式下自动化浏览器。

async_title.py

#!/usr/bin/python

import asyncio

from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as playwright:

        webkit = playwright.webkit
        browser = await webkit.launch()
        page = await browser.new_page()

        url = 'http://webcode.me'
        await page.goto(url)
        title = await page.title()

        print(title)

        await browser.close()

asyncio.run(main())

在这个例子中,我们使用剧作家获得一个网页的标题。我们使用异步API。

$ ./async_title.py
My html page
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值