异步:Python的asyncio/greenlet,Java的CompletableFuture
AI摘要: 本文介绍了Python和Java中实现异步编程的几种方法,包括Python中的threading、asyncio和greenlet,以及Java中的CompletableFuture。文章详细比较了这些工具在控制权、调度方式和生态支持方面的差异,并提供了相应的代码示例来说明它们的使用方法。
Python:threading 和 asyncio 实现异步
使用 threading 模拟异步
import time
import threading
def simulate_task(task_id):
print(f"Task {task_id} started.")
time.sleep(2) # 模拟耗时操作
print(f"Task {task_id} finished.")
start_time = time.time()
threads = []
for i in range(150): # 150个任务
thread = threading.Thread(target=simulate_task, args=(i,))
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
end_time = time.time()
print(f"Total time taken with threading: {end_time - start_time} seconds")
这是比较常规的实现,虽然Python有全局GIL锁,但是在IO密集型任务中问题不算大,只是影响CPU密集型中对多核CPU利用率。
在IO密集型中的多线程实现中,如果线程过多,也会由于频繁的上下文切换而拖累性能。
使用 asyncio
实现异步
import time
import asyncio
async def simulate_task(task_id):
print(f"Task {task_id} started.")
await asyncio.sleep(2) # 模拟耗时操作
print(f"Task {task_id} finished.")
start_time = time.time()
async def main():
tasks = []
for i in range(150): # 150个任务
tasks.append(simulate_task(i))
await asyncio.gather(*tasks)
asyncio.run(main())
end_time = time.time()
print(f"Total time taken with asyncio: {end_time - start_time} seconds")
Asyncio基于协程和事件循环来实现。由于协程的特性,多个协程也不过是在单个线程内部切换,没有上下文切换的损耗,所以想搞多少就搞多少协程,充分压榨CPU。事件循环主要是为了管理多个协程之间的切换。对比上面的Threading版本,速度快0.02s左右。
异步任务嵌套
import asyncio
async def task1():
print("Task 1 start")
await asyncio.sleep(1)
print("Task 1 end")
async def task2():
print("Task 2 start")
await task1() # 调用另一个异步函数
print("Task 2 end")
async def other_task():
for i in range(3):
print(f"Other task is running {i+1}")
await asyncio.sleep(0.5)
async def main():
# 同时运行多个任务
await asyncio.gather(
task2(),
other_task(),
)
asyncio.run(main())
协程阻塞住之后,会自动让出cpu去执行别的任务。就如上面,在task1卡住之后,事件循环会去运行other_task。(不会执行task2的,这个依赖task1的执行,卡在await task1() 这行)
Python:greenlet
协程实现
from greenlet import greenlet
def task_1():
print("Task 1: Start")
g2.switch() # 切换到 task_2
print("Task 1: End")
def task_2():
print("Task 2: Start")
g1.switch() # 切换回 task_1
print("Task 2: End")
g1 = greenlet(task_1)
g2 = greenlet(task_2)
g1.switch() # 启动任务 1
greenlet
也是基于协程的异步工具,不过有了asyncio之后,用的不多了。
对比 greenlet
和 asyncio
:
- 控制权:
greenlet
的控制权是显式的,开发者需要在代码中手动指定任务切换的时机。而asyncio
通过事件循环和await
自动管理任务切换。 - 调度方式:
greenlet
使用协作式调度,需要手动切换任务。asyncio
则是事件驱动的自动调度,适用于 I/O 密集型任务,且简洁高效。 - 生态和支持:
asyncio
是 Python 标准库的一部分,得到了更广泛的支持。greenlet
通常在性能要求较高的底层实现中使用。
Java:使用 CompletableFuture
实现异步
import java.util.concurrent.CompletableFuture;
Blog class AsyncExample {
Blog static CompletableFuture<Void> task1() {
return CompletableFuture.runAsync(() -> {
try {
System.out.println("Task 1 started");
Thread.sleep(2000); // 模拟耗时操作
System.out.println("Task 1 finished");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
Blog static CompletableFuture<Void> task2() {
return task1().thenRun(() -> { // 调用另一个异步函数
System.out.println("Task 2 started");
System.out.println("Task 2 finished");
});
}
Blog static void main(String[] args) {
task2().join(); // 启动任务 2 并等待完成
}
}
CompletableFuture
与协程对比:CompletableFuture
并不是协程,它使用线程池和回调机制来实现异步操作,每个异步任务通常由独立线程执行。Java 中的异步通常通过 ExecutorService
(如 ForkJoinPool
)来管理任务,而不是使用协程。
总结:
- Python:
asyncio
是标准的异步编程方式,基于协程和事件循环,适用于 I/O 密集型任务。greenlet
提供了一种低级的协程实现,控制权由开发者显式管理。两者在生态和支持上有不同的使用场景。 - Java:
CompletableFuture
不是协程,而是基于线程池和回调机制的异步实现,常用于处理异步任务。与 Python 的协程不同,Java 更倾向于通过线程池管理并发任务。