Python协程和线程的混合使用

简介

一般在项目中会根据选择的框架,按照框架的逻辑实现业务,如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中异步执行。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值