一位C#程序员试图学习一些Python。我正在尝试在运行CPU密集计算的同时让一个IO绑定的异步方法在后台默默运转。在C#中,我通常会设置可等待对象,然后启动CPU密集代码,然后等待IO任务完成,最后组合结果。
以下是我在C#中的做法:
static async Task DoStuff() {
var ioBoundTask = DoIoBoundWorkAsync();
int cpuBoundResult = DoCpuIntensizeCalc();
int ioBoundResult = await ioBoundTask.ConfigureAwait(false);
Console.WriteLine($"The result is {cpuBoundResult + ioBoundResult}");
}
static async Task<int> DoIoBoundWorkAsync() {
Console.WriteLine("Make API call...");
await Task.Delay(2500).ConfigureAwait(false); // non-blocking async call
Console.WriteLine("Data back.");
return 1;
}
static int DoCpuIntensizeCalc() {
Console.WriteLine("Do smart calc...");
Thread.Sleep(2000); // blocking call. e.g. a spinning loop
Console.WriteLine("Calc finished.");
return 2;
}
以下是相应的Python代码
import time
import asyncio
async def do_stuff():
ioBoundTask = do_iobound_work_async()
cpuBoundResult = do_cpu_intensive_calc()
ioBoundResult = await ioBoundTask
print(f"The result is {cpuBoundResult + ioBoundResult}")
async def do_iobound_work_async():
print("Make API call...")
await asyncio.sleep(2.5) # non-blocking async call
print("Data back.")
return 1
def do_cpu_intensive_calc():
print("Do smart calc...")
time.sleep(2) # blocking call. e.g. a spinning loop
print("Calc finished.")
return 2
await do_stuff()
请注意,CPU密集型任务由阻塞式的睡眠表示,无法等待完成,而IO绑定型任务则由可等待的非阻塞式睡眠表示。
在C#中,这需要2.5秒才能运行,在Python中需要4.5秒。区别在于C#直接运行异步方法,而Python只有在遇到"await"关键字时才开始运行该方法。如下所示的输出证实了这一点。如何实现所需结果?如果可能,请提供适用于Jupyter Notebook的代码。
--- C# ---
Make API call...
Do smart calc...
Calc finished.
Data back.
The result is 3
--- Python ---
Do smart calc...
Calc finished.
Make API call...
Data back.
The result is 3
更新1
受knh190答案的启发,似乎我可以使用asyncio.create_task(...)
实现大部分功能。这将实现所需的结果(2.5秒):首先,异步代码开始运行;接着,阻塞的CPU代码同步运行;第三步等待异步代码执行完毕;最后将结果组合在一起。为了让异步调用真正开始运行,我不得不添加await asyncio.sleep(0)
,这感觉像一个可怕的hack。我们能否在不这样做的情况下启动任务?肯定有更好的方法...
async def do_stuff():
task = asyncio.create_task(do_iobound_work_async())
await asyncio.sleep(0) # <~~~~~~~~~ This hacky line sets the task running
cpuBoundResult = do_cpu_intensive_calc()
ioBoundResult = await task
print(f"The result is {cpuBoundResult + ioBoundResult}")
asyncio.ensure_future
。 - Dan D.max(i%3 for i in range(10000000))
之类的内容替换time.sleep(2)
。 - Big AL