Python的高并发困扰了我很长时间。在这里我总结一下我目前粗浅的理解,欢迎大家来讨论。Python的thread并做不到真正的parallelism,因为有一个GIL(Global Interpreter Lock),所以同时只能执行一个thread的任务。对于计算量大的任务,没有必要开多个thread,因为来回切换线程也有overhead。那什么时候用呢?比如说I/O bottleneck的任务,因为大部分时候在等待。一个thread正在等待的过程中如果CPU切换到另一个thread去执行计算,那么就相当于利用了这个等待的时间,因此速度会大幅度提升。例如flask就是用multi threading的。

那么如果计算量任务大的任务怎么办,可以用multi processing来解决。这时候能提升多少取决于硬件上的限制。

asyncio里的async/await也是适用于等待I/O时间偏长的任务。相较于multi threading的优势在于,你可以通过await关键词告诉CPU啥时候去干点别的。CPU看到await就知道这里估计要等一会,这种关键词比multi threading里面随时跑去执行别的任务来得更靠谱一些(multi threading里面的调度完全不清楚,也许有一些优化,但是毕竟不如await来得更精细)。asyncio里一个不太方便的地方时如果一个function本身是async,那么他能call的function也必须是async。有时候我们要用library里的function/method并不是async的,那怎么办呢。我找到了一个potential solution,用到了concurrent futures:


上周末自己写了一个script来模拟producer/consumer model,其中consumer会有很多blocking operation。我试图通过asyncio来提升performance。

其中花了很长时间figure out怎么能保证所有coroutine都被schedule到。这是一个有点tricky的问题,因为如果coroutine的数量少了,会出现所有coroutine都在wait blocking IO的情况。但是如果coroutine的数量多了,就怕有的coroutine一直不被执行。对于producer consumer model来说,最怕就是取出来一个token,这个coroutine consume到一半进入了await,然后这个coroutine一直不被execute,那么这个token从被生产出来到被完成计算的时间跨度就会非常长。我后来找到了可以用await asyncio.sleep(0)来解决这个问题,把控制权交还给event loop, 这样可以模拟一个近似round robin的情况。当然在这种情况,coroutine数量的依旧是需要细心调试的。