简介
一般在项目中会根据选择的框架,按照框架的逻辑实现业务,如fastapi、Aiohttp是基于异步编程的框架,Django、Flask是基于WSGI同步处理的框架。
一、协程的使用
协程的使用主要通过两种方式:1.yield关键字,2.asyncio模块
1.yield关键字
项目中比较典型的应用,在使用数据库查询时对session的统一处理:
def make_session():
session = session_local()
try:
yield session
finally:
session.close()
在调用时使用with方式:
with make_session() as db_session:
return db_session.query(*).filter(*).first()
在使用完session后,会自动运行finally进行close
2.asyncio模块
项目中简单的异步任务实现:
import asyncio
async def task1():
await asyncio.sleep(1)
async def task2():
await asyncio.sleep(2)
async def main():
task_one = asyncio.create_task(task1())
task_two = asyncio.create_task(task2())
await task_one
await task_two
asyncio.run(main())
当需要让两个任务并行时,可以对main做如下修改:
async def main():
await asyncio.gather(task1(), task2())
如果在项目中需要实现常住任务有以下两种方式(两种方式在调度的时有差异):
async def loop_task():
while True:
...
loop = asyncio.get_event_loop()
# 无限循环
loop.run_until_complete(loop_task())
# forever
loop.create_task(loop_task())
loop.run_forever()
二、线程的使用
项目中用到线程threading模块,简单的线程启动方式:
import threading
import time
def print_msg(arg):
time.sleep(1)
print(f'thread {arg} running....')
if __name__ == '__main__':
for i in range(10):
t = threading.Thread(target=print_msg, args=(i,))
t.start()
更多的还是使用到线程池:
from concurrent.futures import ThreadPoolExecutor
# 创建线程池,线程个数
executor = ThreadPoolExecutor(max_workers=2)
task1 = executor.submit(print_msg, (11))
task2 = executor.submit(print_msg, (12))
print(task1.result())
print(task2.result())
三、协程和线程的混合使用
由于项目业务的需求,需要在两个线程中各自运行不同的异步任务,但有需要有两线程之间的业务交互,进行了多次验证:
1.两条任务线都使用无限循环
async def connect_to_server():
while True:
...
def socket_client_start():
socket_loop = asyncio.new_event_loop()
asyncio.set_event_loop(socket_loop)
socket_loop.run_until_complete(connect_to_server())
socket_thread = threading.Thread(target=socket_client_start)
socket_thread.start()
async def connect_to_browser():
while True:
...
def browser_client_start():
browser_loop = asyncio.new_event_loop()
asyncio.set_event_loop(browser_loop)
browser_loop.run_until_complete(connect_to_browser())
browser_thread = threading.Thread(target=browser_client_start)
browser_thread.start()
此时业务在各自线程中都能运行正常,涉及到跨线程创建task时loop无法运行。
跨线程调用实例:
async def update_status(status):
...
asyncio.run_coroutine_threadsafe(update_status(1), loop)
在Python Docs的api中看到run_forever的使用,对上述使用做调整:
def socket_client_start():
socket_loop = asyncio.new_event_loop()
asyncio.set_event_loop(socket_loop)
socket_loop.create_task(connect_to_server())
socket_loop.run_forever()
...
def browser_client_start():
browser_loop = asyncio.new_event_loop()
asyncio.set_event_loop(browser_loop)
browser_loop.create_task(connect_to_browser())
browser_loop.run_forever()
调整后,再进行任务提交时能够正常执行新添加到事件循环的任务。
原因分析:在循环事件loop.run_until_complete执行的无限循环阻塞了新任务的提交,导致任务无法执行,需要将无限循环任务迁移到task中异步执行。