为什么 asyncio 单线程 速度还能那么快

我对于asyncio的了解是 通过await去处理阻塞的corountines,直接处理下一个消息,但因为不是多线程,感觉还是在一个一个处理啊,为什么会比multithread速度快呢?
比如:

Number of Process Multiprocessing Asyncio
2 25.5s 7.5s
4 15.4s 7.0s
8 11.5s 7.2s
回答问题 的 注意下素质好么 不用无聊灌水浪费大家时间

莫凡python asyncio教程那边过来的吧?

我这两天也在学习asyncio,之前都是用threading写爬虫,刚拿我之前的一个贴吧爬虫的需求尝试了一下,确实是快,爬某吧会员列表,协程的速度比我线程的速度要快好几倍:

协程与多线程速度对比1

(测试1,爬1000张页面。aio.py脚本是协程;asy2.py脚本是多线程,线程数是15)

协程与多线程速度对比2

(测试2,爬10000张页面。aio.py脚本是协程;asy2.py脚本是多线程,线程数是100)

至于为什么快,我的理解是,协程更适合高IO操作,低计算密集的程序,而爬虫本质上其实都是IO操作(请求网络内容并下载是网络IO,下载下来并写入硬盘也是本地IO)。

而多线程之所以在这方面没有协程快,主要是因为:

线程开销更大(主要在于占用cpu资源,以及多线程之间上下文切换的开销,参考:多线程的线程开销

而实际上爬虫在干什么呢?不停下载数据而已,基本都不需要cpu计算,所以要cpu的开销做什么呢?


最后再来看一下跑2个脚本时我的电脑资源占用对比:

跑协程时

跑多线程(线程数100)时

所以为什么协程快?这就一目了然了。

(P.S. 用asyncio需要确保写对了才行,不然可能会反而比多线程慢,要注意。这方面我也还在学习中。)

温馨提示:答案为网友推荐,仅供参考
第1个回答  2019-03-18

这个也要看他的主频是多高了.比如单核CPU3.06就比双核2.6的主频要高,但是单线程是运行一个程序或少个程序能显示出来他的速度,运行东西多了的话就会被多线程给比下去了.

追问

那请问asyncio是单线程么 我还是不太理解

第2个回答  2019-03-18
介绍.
MMO游戏毫无疑问是当前主流趋势,不管从技术角度还是从流行趋势.曾几何时,写一个MMO游戏是需要大量的预算和非常复杂的底层的编程技术.最近,事情发生变化了.很多基于动态语言的现在框架可以做到处理数以千计的玩家连接.(数以千计,咋一听起来有点挫啊,可是仔细想想一个单独的进程也差不多就是这个级别,大型的MMO都是在这个基础上做多进程扩展).同时html 5和WebSockets标准能够实现网页运行的实时画面游戏.
python可能不是创建可扩展的非阻塞服务器的最流行的工具,尤其和node.js对比.但是最新版本的python瞄准了这一问题.asyncio和async/await能够是异步的语言看起来和常规的代码一样直接.所以我们来通过这些新特性来演示一下如何创建一个MMO游戏.
变成异步
一个gameserver能够处理大量玩家的并行连接,同时要做到实时.一个方法是创建线程,可是这种方法并不适用.运行几千个线程,cpu就要不停的切换线程(context switching),这样就非常抵消.如果用多进程呢?那更糟糕,因为需要占用更多的内存.使用pyuthon还有一个更大的问题 - 通常python解释器(CPython)设计上不能做到真正的多线程,它的目标是单线程的效率. 这就是它使用GIL(global interpreter lock)的原因,这就使得python不同同时运行代码,来防止共享对象的使用. 一般情况下,解释器切换到另一个线程发生在当前线程正咋等待io或者其他.这确实能够做到费阻塞的io.因为只阻塞在一个线程里.然而,这并不能利用多线程的优势,因为不能够同时运行代码,即使是在多核cpu上面.其实呢, 在单线程里面也是完全可以做到非组赛io的,这样就避免了大量的context-switching.
这种单线程非阻塞的实现通过纯python就可以做到.你需要的是select.你自己要写一个事件循环.这种方法需要你把逻辑都写在一个地方, 然而你的应用会很快变成非常复杂的状态机.有很多框架来简化这个方法,最流行的是tornado和twisted.他们都是通过回调实现了非常复杂的协议.(有点像node.js)这些框架运行自己的事件循环,在特定事件发生时调用你定义的回调.虽然这样已经非常好了,但是这个风格是callback,代码会变得脆弱(不至于吧).和这个相对应的是同步代码.为什么不在一个线程里做到这些呢?
那么我就就要讨论一个新概念, microthreads,(是叫微线程么).意思就是在单线程里面同步运行很多任务.当你调用一个阻塞的任务,背后的"manager"会运行一个事件循环.当一个事件发生,这个manager就会通知等待这个事件的任务.这个任务就继续运行直到遇到一个阻塞,然后有把运行任务交给manager.

作者:default
链接:https://www.jianshu.com/p/8a51f641da07
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
第3个回答  2019-03-18
我们首先要明白,上边的种种分析,都是为了营造一个Redis很快的氛围!官方FAQ表示,因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了(毕竟采用多线程会有很多麻烦!)。
第4个回答  2019-03-18
首先需要明确一点,asyncio使用单线程、单个进程的方式切换(通常程序等待读或写数据时就是切换上下文的时机),那这样效率高嘛?实践是检验真理的唯一标准。我们用之前介绍的concurrent.futures和asyncio分别试验下。当然下面例子的结果仅供参考,因为无法保证被请求的网站的服务水平,这会造成对结果或多或少有影响,可以多跑几次综合的来看。仅供参考!
相似回答