python3 协程实战(python3经典编程案例)

简介: 该文章通过多个实战案例介绍了如何在Python3中使用协程来提高I/O密集型应用的性能,利用asyncio库以及async/await语法来编写高效的异步代码。

一. 定义协程

协程是轻量级线程,拥有自己的寄存机上下文和栈。
协程调度切换时,将寄存器上下文和栈保存到其他地方,再切回来时,恢复先前保存的寄存器上下文和栈。

协程的应用场景:I/O密集型任务,和多线程类似,但协程调用时在一个线程内进行的,是单线程,切换的开销小,因此,效率上略高于多线程。

python3.4加入了协程,以生成器对象为基础,python3.5加了async/await,使用协程更加方便。

python中使用协程最方便的库是asyncio,引入该库才能使用async和await关键字

  • async: 定义一个协程;async定义的方法无法直接执行,必须注册到时间循环中才能执行。
  • await: 用于临时挂起一个函数或方法的执行。

根据官方文档,await后面的对象必须是如下类型之一:

  • 一个原生的coroutine对象;
  • 一个由types.coroutine()修饰的生成器,这个生成器可以返回coroutine对象;
  • 一个包含await方法的对象返回的一个迭代器。

案例1:

import asyncio
import time


async def task():
    print(f"{time.strftime('%H:%M:%S')} task 开始 ")
    time.sleep(2)
    print(f"{time.strftime('%H:%M:%S')} task 结束")


coroutine = task()
print(f"{time.strftime('%H:%M:%S')} 产生协程对象 {coroutine},函数并未被调用")
loop = asyncio.get_event_loop()
print(f"{time.strftime('%H:%M:%S')} 开始调用协程任务")
start = time.time()
loop.run_until_complete(coroutine)
end = time.time()
print(f"{time.strftime('%H:%M:%S')} 结束调用协程任务, 耗时{end - start} 秒")

案例2:
为任务绑定回调函数

import asyncio
import time


async def _task():
    print(f"{time.strftime('%H:%M:%S')} task 开始 ")
    time.sleep(2)
    print(f"{time.strftime('%H:%M:%S')} task 结束")
    return "运行结束"


def callback(task):
    print(f"{time.strftime('%H:%M:%S')} 回调函数开始运行")
    print(f"状态:{task.result()}")


coroutine = _task()
print(f"{time.strftime('%H:%M:%S')} 产生协程对象 {coroutine},函数并未被调用")
task = asyncio.ensure_future(coroutine)  # 返回task对象
task.add_done_callback(callback)  # 为task增加一个回调任务
loop = asyncio.get_event_loop()
print(f"{time.strftime('%H:%M:%S')} 开始调用协程任务")
start = time.time()
loop.run_until_complete(task)
end = time.time()
print(f"{time.strftime('%H:%M:%S')} 结束调用协程任务, 耗时{end - start} 秒")

二. 并发

如果需要执行多次协程任务并尽可能的提高效率,这时可以定义一个task列表,然后使用asyncio的wait()方法执行即可。

import asyncio
import time


async def task():
    print(f"{time.strftime('%H:%M:%S')} task 开始 ")
    # 异步调用asyncio.sleep(1):
    await asyncio.sleep(2)
    # time.sleep(2)
    print(f"{time.strftime('%H:%M:%S')} task 结束" )

# 获取EventLoop:
loop = asyncio.get_event_loop()
# 执行coroutine
tasks = [task() for _ in range(5)]
start = time.time()
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
end = time.time()
print(f"用时 {end-start} 秒")

三. 异步请求

先启动一个简单的web服务器

from flask import Flask
import time

app = Flask(__name__)


@app.route('/')
def index():
    time.sleep(3)
    return 'Hello World!'


if __name__ == '__main__':
    app.run(threaded=True)

案例1:请求串行走下来,没有实现挂起。

import asyncio
import requests
import time

start = time.time()


async def request():
    url = 'http://127.0.0.1:5000'
    print(f'{time.strftime("%H:%M:%S")} 请求 {url}')
    response = requests.get(url)
    print(f'{time.strftime("%H:%M:%S")} 得到响应 {response.text}')

tasks = [asyncio.ensure_future(request()) for _ in range(5)]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

end = time.time()
print(f'耗时 {end - start} 秒')

使用await将耗时等待的操作挂起,让出控制权。
当协程执行时遇到await, 时间循环就会将本协程挂起,转而去执行别的协程,知道其他的协程挂起或者执行完毕。

案例2:异步IO请求实例
将请求页面的代码封装成一个coroutine对象,在requests中尝试使用await挂起当前执行的I/O.

import asyncio
import requests
import time


async def get(url):
    return requests.get(url)


async def request():
    url = "http://127.0.0.1:5000"
    print(f'{time.strftime("%H:%M:%S")} 请求 {url}')
    response = await get(url)
    print(f'{time.strftime("%H:%M:%S")} 得到响应 {response.text}')


start = time.time()
tasks = [asyncio.ensure_future(request()) for _ in range(5)]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
end = time.time()
print(f"耗时 {end - start} 秒")

上面的带动并未达到预期的并发效果。原因是requests不是异步请求,无论如何改封装都无济于事,因此需要找真正的IO请求,aiohttp是一个支持异步请求的库,可以用它和anyncio配合,实现异步请求操作。
案例3:使用aiohttp库

import asyncio
import aiohttp
import time

now = lambda: time.strftime("%H:%M:%S")


async def get(url):
    session = aiohttp.ClientSession()
    response = await session.get(url)
    result = await response.text()
    await session.close()
    return result


async def request():
    url = "http://127.0.0.1:5000"
    print(f"{now()} 请求 {url}")
    result = await get(url)
    print(f"{now()} 得到响应 {result}")


start = time.time()
tasks = [asyncio.ensure_future(request()) for _ in range(5)]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

end = time.time()
print(f"耗时 { end - start } 秒")

运行结果符合预期要求,耗时由15秒变成了3秒,实现了并发访问。

将任务数5改成100,运行时间也在3秒多一点,多出来的时间就是I/O时延了。
可见,使用异步协程之后,几乎可以在相同时间内实现成百上千次的网络请求。

相关文章
|
18天前
|
Unix Linux 程序员
[oeasy]python053_学编程为什么从hello_world_开始
视频介绍了“Hello World”程序的由来及其在编程中的重要性。从贝尔实验室诞生的Unix系统和C语言说起,讲述了“Hello World”作为经典示例的起源和流传过程。文章还探讨了C语言对其他编程语言的影响,以及它在系统编程中的地位。最后总结了“Hello World”、print、小括号和双引号等编程概念的来源。
102 80
|
7天前
|
Python
[oeasy]python055_python编程_容易出现的问题_函数名的重新赋值_print_int
本文介绍了Python编程中容易出现的问题,特别是函数名、类名和模块名的重新赋值。通过具体示例展示了将内建函数(如`print`、`int`、`max`)或模块名(如`os`)重新赋值为其他类型后,会导致原有功能失效。例如,将`print`赋值为整数后,无法再用其输出内容;将`int`赋值为整数后,无法再进行类型转换。重新赋值后,这些名称失去了原有的功能,可能导致程序错误。总结指出,已有的函数名、类名和模块名不适合覆盖赋新值,否则会失去原有功能。如果需要使用类似的变量名,建议采用其他命名方式以避免冲突。
29 14
|
16天前
|
分布式计算 大数据 数据处理
技术评测:MaxCompute MaxFrame——阿里云自研分布式计算框架的Python编程接口
随着大数据和人工智能技术的发展,数据处理的需求日益增长。阿里云推出的MaxCompute MaxFrame(简称“MaxFrame”)是一个专为Python开发者设计的分布式计算框架,它不仅支持Python编程接口,还能直接利用MaxCompute的云原生大数据计算资源和服务。本文将通过一系列最佳实践测评,探讨MaxFrame在分布式Pandas处理以及大语言模型数据处理场景中的表现,并分析其在实际工作中的应用潜力。
53 2
|
7月前
|
Go Python
使用python实现一个用户态协程
【6月更文挑战第28天】本文探讨了如何在Python中实现类似Golang中协程(goroutines)和通道(channels)的概念。文章最后提到了`wait_for`函数在处理超时和取消操作中的作
63 1
使用python实现一个用户态协程
|
6月前
|
数据库 开发者 Python
实战指南:用Python协程与异步函数优化高性能Web应用
【7月更文挑战第15天】Python的协程与异步函数优化Web性能,通过非阻塞I/O提升并发处理能力。使用aiohttp库构建异步服务器,示例代码展示如何处理GET请求。异步处理减少资源消耗,提高响应速度和吞吐量,适用于高并发场景。掌握这项技术对提升Web应用性能至关重要。
96 10
|
6月前
|
数据处理 Python
深入探索:Python中的并发编程新纪元——协程与异步函数解析
【7月更文挑战第15天】Python 3.5+引入的协程和异步函数革新了并发编程。协程,轻量级线程,由程序控制切换,降低开销。异步函数是协程的高级形式,允许等待异步操作。通过`asyncio`库,如示例所示,能并发执行任务,提高I/O密集型任务效率,实现并发而非并行,优化CPU利用率。理解和掌握这些工具对于构建高效网络应用至关重要。
64 6
|
6月前
|
大数据 数据处理 API
性能飞跃:Python协程与异步函数在数据处理中的高效应用
【7月更文挑战第15天】在大数据时代,Python的协程和异步函数解决了同步编程的性能瓶颈问题。同步编程在处理I/O密集型任务时效率低下,而Python的`asyncio`库支持的异步编程利用协程实现并发,通过`async def`和`await`避免了不必要的等待,提升了CPU利用率。例如,从多个API获取数据,异步方式使用`aiohttp`并发请求,显著提高了效率。掌握异步编程对于高效处理大规模数据至关重要。
61 4
|
6月前
|
设计模式 机器学习/深度学习 测试技术
设计模式转型:从传统同步到Python协程异步编程的实践与思考
【7月更文挑战第15天】探索从同步到Python协程异步编程的转变,异步处理I/O密集型任务提升效率。async/await关键词定义异步函数,asyncio库管理事件循环。面对挑战,如思维转变、错误处理和调试,可通过逐步迁移、学习资源、编写测试和使用辅助库来适应。通过实践和学习,开发者能有效优化性能和响应速度。
63 3
|
6月前
|
调度 Python
揭秘Python并发编程核心:深入理解协程与异步函数的工作原理
【7月更文挑战第15天】Python异步编程借助协程和async/await提升并发性能,减少资源消耗。协程(async def)轻量级、用户态,便于控制。事件循环,如`asyncio.get_event_loop()`,调度任务执行。异步函数内的await关键词用于协程间切换。回调和Future对象简化异步结果处理。理解这些概念能写出高效、易维护的异步代码。
69 2
|
6月前
|
Python
从零到一:构建Python异步编程思维,掌握协程与异步函数
【7月更文挑战第15天】Python异步编程提升效率,通过协程与异步函数实现并发。从async def定义异步函数,如`say_hello()`,使用`await`等待异步操作。`asyncio.run()`驱动事件循环。并发执行任务,如`asyncio.gather()`同时处理`fetch_data()`任务,降低总体耗时。入门异步编程,解锁高效代码。
84 1