python异步(Async)编程
python异步(Async)编程
异步和同步的概念
同步:一个一个步骤的往下执行。只有在上一步完成后,程序才会进入下一个步骤。例子:批处理程序、命令行程序
异步:不用于同步的是,系统不会等待执行步骤完成后再继续执行下一个步骤。
异步的优势
同步网络服务器,在需要处理大量的访问请求的时候,服务器有多少个处理单元就只能处理多少个请求,且这些请求处理会涉及到网络速度、文件IO速度、数据库处理速度等不需要处理单元过多干涉的操作。结果会导致同步网络服务器陷入低效率的工作状态。而异步编程技术允许您的程序通过释放 CPU 来做其他工作来利用相对较慢的 IO 进程。
使用例子
同步程序
import queue
def task(name, work_queue):
if work_queue.empty():
print(f"Task {name} nothing to do")
else:
while not work_queue.empty():
count = work_queue.get()
total = 0
print(f"Task {name} running")
for x in range(count):
total += 1
print(f"Task {name} total: {total}")
def main():
"""
This is the main entry point for the program
"""
# Create the queue of work
work_queue = queue.Queue()
# Put some work in the queue
for work in [15, 10, 5, 2]:
work_queue.put(work)
# Create some synchronous tasks
tasks = [(task, "One", work_queue), (task, "Two", work_queue)]
# Run the tasks
for t, n, q in tasks:
t(n, q)
if __name__ == "__main__":
main()
非阻塞调用(异步友好)的协作并发程序
import asyncio
from codetiming import Timer
# async定义为异步函数
async def task(name, work_queue):
timer = Timer(text=f"Task {name} elapsed time: {{:.1f}}")
while not work_queue.empty():
delay = await work_queue.get()
print(f"Task {name} running")
timer.start()
await asyncio.sleep(delay) # 创建一个非阻塞延迟,执行上下文切换回调用者main()
timer.stop()
async def main():
"""
This is the main entry point for the program
"""
# Create the queue of work
work_queue = asyncio.Queue()
# Put some work in the queue
for work in [15, 10, 5, 2]:
await work_queue.put(work) # 到达await关键字,发生上下文切换
# Run the tasks
with Timer(text="\nTotal elapsed time: {:.1f}"):
await asyncio.gather(
asyncio.create_task(task("One", work_queue)),
asyncio.create_task(task("Two", work_queue)),
)
if __name__ == "__main__":
asyncio.run(main())
异步(非阻塞)HTTP调用
import asyncio
import aiohttp
from codetiming import Timer
async def task(name, work_queue):
timer = Timer(text=f"Task {name} elapsed time: {{:.1f}}")
async with aiohttp.ClientSession() as session:
while not work_queue.empty():
url = await work_queue.get()
print(f"Task {name} getting URL: {url}")
timer.start()
async with session.get(url) as response:
await response.text()
timer.stop()
async def main():
"""
This is the main entry point for the program
"""
# Create the queue of work
work_queue = asyncio.Queue()
# Put some work in the queue
for url in [
"http://google.com",
"http://yahoo.com",
"http://linkedin.com",
"http://apple.com",
"http://microsoft.com",
"http://facebook.com",
"http://twitter.com",
]:
await work_queue.put(url)
# Run the tasks
with Timer(text="\nTotal elapsed time: {:.1f}"):
await asyncio.gather(
asyncio.create_task(task("One", work_queue)),
asyncio.create_task(task("Two", work_queue)),
)
if __name__ == "__main__":
asyncio.run(main())
拓展概念
可等待的对象氛围三种类型:协程、任务和Future.
当遇到可等待对象,进程会发生上下文切换(任务调度)
协程
async def main():
print('runing...')
await asyncio.sleep(2)
注意,直接调用协程并不会使其调度执行。
有三种方式运行一个协程:1. asyncio.run(main()) 2. await main() 3. asyncio.create_task(main())
任务
用来“并行的”调度协程。
当一个协程通过 asyncio.create_task()
等函数被封装为一个任务,该协程会被自动调度执行。
task = asyncio.create_task(main())
await task
Future
通常情况下 没有必要 在应用层级的代码中创建 Future 对象。这是一种特殊的 低层级 可等待对象。(有什么应用场景,我目前还不清楚)
API
创建任务
asyncio.create_task(main())
休眠:挂起当前任务,以允许其他任务运行。
await asyncio.sleep(1)
“并发”运行任务
await asyncio.gather(
asyncio.create_task(task("One", work_queue)),
asyncio.create_task(task("Two", work_queue)),
)
总结
异步编程经常用在存在很多IO操作的场景。我们要记住用asyncio来并发运行任务,仍然是单进程程序,与同步单进程不同的是,有任务调度的概念,且可等待对象协程被作为一个任务调度。
参考
[1] Getting Started With Async Features in Python. LINK
[2] 协程与任务高层级API介绍