了解异步

最后更新时间: 2018-07-01 | 作者: AberSheeran | 捐助

在几个月前我学习的时候,看网上的一些博客,看的头晕脑胀,各种博客都是吹异步怎么好用,怎么用。对异步这玩意的原理还是不知道为什么,遂放弃。

直到,上个月手撸异步Socket的时候,了解到了epoll和select,才明悟这是个什么玩意。

众所周知,所有的网络爬虫都是基于TCP Socket的。这个玩意有一个很致命的缺点,当对方尚未响应的时候,你只能等着对方响应。一般来说,单线程的同步网络爬虫的很大一部分时间都浪费在等待响应上了。所以很多时候,我们使用多线程来请求数据,虽然线程间切换也需要时间,但这个时间远低于网络等待的时间,所以能够充分利用自身的网络。多线程的缺陷也很明显,占用资源过多。

而其他的IO操作,如磁盘IO,也与网络IO有着相同的问题。

为了解决这个问题,就有了异步。所谓异步,就是把任务加进任务池里,然后不断遍历查询有没有任务可以执行。这样,就能在一个线程里,进行多个IO操作而不会阻塞整个线程的执行(因为当一个IO操作阻塞时,就去尝试执行其他IO操作了)。当然,如果在你异步执行的过程里,有特别耗时的同步操作,也会阻塞其他异步任务的执行,因为这个同步操作会阻塞整个线程。这就是所谓的,一步是异步,步步须异步。

那么所谓的loop(事件循环)就很明了了,其实就是不断地检查任务池里有没有任务可以执行。而异步阻塞就是,==这个地方我要阻塞一些时间,这段时间你给我执行其他的任务==。

Linus说得好:

Talk is cheap, show me the code.

样例

我们来看这段异步代码:fun_print函数会在执行输出之前,异步阻塞一段时间。根据我上面所述,那么输出结果必然是从小到大的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import asyncio
import random
import time


async def fun_print(delay):
    await asyncio.sleep(delay)
    print(delay)


tasks = list()
for i in range(10):
    tasks.append(asyncio.ensure_future(fun_print(random.randint(0, 10))))

loop = asyncio.get_event_loop()
time.clock()
loop.run_until_complete(asyncio.wait(tasks))
print("loop的执行时间:", time.clock())

运行之后可以看到loop的执行时间由阻塞时间最长的那个异步任务决定。但如果我们把await asyncio.sleep(delay)换成time.sleep(delay),那么loop的执行时间会是所有任务的和,并且执行顺序也不再也是从小到大,而是由进入loop的顺序决定。

标签: Python异步
收录于#杂记