面试题答案
一键面试设计思路
- 任务划分:将应用程序的任务分为两类,CPU密集型任务和I/O密集型任务。CPU密集型任务使用多线程处理,I/O密集型任务使用异步I/O处理。因为Python的全局解释器锁(GIL)限制了多线程在CPU密集型任务上的并行性,但在I/O操作时,线程会释放GIL,所以多线程适用于I/O操作。而异步I/O通过事件循环和协程,能更高效地处理大量I/O操作。
- 线程池与异步I/O结合:利用
concurrent.futures
模块中的ThreadPoolExecutor
创建线程池,将CPU密集型任务提交到线程池执行。对于I/O密集型任务,使用asyncio
库创建协程,并通过asyncio
的事件循环来管理和调度这些协程。 - 协调机制:在主线程中创建
asyncio
的事件循环,在事件循环中可以通过run_in_executor
方法将CPU密集型任务提交到线程池执行,同时继续执行异步I/O任务。这样可以充分利用多线程和异步I/O的优势,提高整体性能。
关键代码片段
import asyncio
import concurrent.futures
import time
# CPU密集型任务
def cpu_bound_task():
time.sleep(1)
return "CPU bound task result"
# I/O密集型任务(模拟)
async def io_bound_task():
await asyncio.sleep(1)
return "I/O bound task result"
async def main():
# 创建线程池
with concurrent.futures.ThreadPoolExecutor() as executor:
loop = asyncio.get_running_loop()
# 提交CPU密集型任务到线程池
cpu_task = loop.run_in_executor(executor, cpu_bound_task)
# 执行I/O密集型任务
io_task = io_bound_task()
# 等待所有任务完成
cpu_result, io_result = await asyncio.gather(cpu_task, io_task)
print(cpu_result)
print(io_result)
if __name__ == "__main__":
asyncio.run(main())
在上述代码中:
cpu_bound_task
函数模拟一个CPU密集型任务,通过time.sleep
模拟计算耗时。io_bound_task
函数模拟一个I/O密集型任务,使用asyncio.sleep
模拟I/O等待时间。- 在
main
函数中,首先创建了一个ThreadPoolExecutor
线程池。然后通过loop.run_in_executor
将cpu_bound_task
提交到线程池执行,同时创建io_bound_task
协程。最后使用asyncio.gather
等待这两个任务都完成,并输出结果。 - 最后通过
asyncio.run(main())
启动事件循环执行整个流程。