最牛的异步程序写法——协程

要讲清楚协程的原理非常难,我也只是了解一点皮毛,不过我还是很想把这些皮毛跟大家分享一下,首先跟大家说一下多进程、多线程和协程的原理。

进程是系统分配资源的最小单位,多进程耗费系统资源,一般不会使用,而一个进程又可以包含多个线程,多线程要比多进程节省资源,在Python中由于全局解释器锁的存在,一个进程中同一时间最多只能执行一个线程,所以Python原生是不支持异步并发的。

协程是线程的一种实现方式,协程就是单线程,那么协程和普通的单线程有什么区别那?举个简单的例子,华罗庚时间利用法,假设小明做作业要10分钟,煮饭要20分钟,洗衣服要10分钟,如果采用单线程的模式,这一套流程要花40分钟,而我们又都知道,煮饭的同时也可以做作业和洗衣服,那么最短的耗时应该是20分钟,协程就是这个原理,当某个任务遇到阻塞时,它会自动切换到其他任务,当其他任务遇到阻塞时再切换到另一个任务,相当于是异步的。

而多线程是什么原理那?多线程就是先花两分钟写作业,再花两分钟煮饭,再花两分钟洗衣服,以此类推,把时间分成若干等份,每一份时间执行不同的任务,实际上并不一定就节约时间,但是却实现了并发,那么协程的代码应该怎么写那?我给大家简单的写一下。

这是一个简单的协程下载文件的程序,看起来代码比较多,其实异步的代码就是要比同步复杂,我大体给大家说一下这个代码的思路。

首先我们需要写一个异步执行函数,这个函数在普通函数的关键字加async;创建一个异步事件循环,写法为asyncio.get_event_loop(),返回值为一个事件循环。在循环中调用异步执行函数,将函数的返回值写为c;此时可以直接将c加入事件循环中,但是由于后期我们可能要写回调函数,所以可以先将c封装到一个future对象中,使用asyncio.ensure_furtue(c),返回值谢伟task;将所有的task保存到一个列表中;()将任务列表加入事件循环,使用loop.run_until_complete(asyncio.wait(task_list)),如果不是任务列表,可以直接将task当作参数放入loop.run_until_complete()中。异步执行函数中,用async标记异步,用await标记等待,需要大量使用with上下文管理器。一般来讲,如果写异步的爬虫可以直接用scrapy,因为scrapy默认就开了多线程,像asyncio太过于复杂,会用当然是最好的。