异步方法介绍(一)
不知道小伙伴们听到异步开不开心,不管你们开不开心,反正讲这个我很爽,虽然我异步造诣不高。
我们知道大部分代码都是同步的,同步就是按照代码的逻辑顺序执行,要等上一条语句执行完了,才能执行下一条语句,这里恶心人的事情就是有些代码要等待,同步没有办法,只能等待,然后接着执行。异步就不一样了,比如这行代码要等待,不好意思,我不等你,我先做后面的事情,你什么时候完事了告诉我一下就行,可以想象到异步可以提高代码的运行效率。
有些小伙伴总喜欢把异步和多线程放在一起,我只能说它们是两种不同的概念,多线程可以简单理解为多个人做很多事情,异步可以简单理解为一个人有条理地做很多事情,一个线程的同步可以简单理解为一个简单的人傻傻地做某些事情。然后呢在python中异步写的接口和线程确实很类似,可能这也迷惑了很多人,但是它们本质上确实不一样。
异步在python中已经很多年了,但是知名度不是很高,可能大家不太习惯转变思维和用异步编程用的相对少一些,包括本人用的也比较少。异步一般是一个线程做事情,如何合理分配很重要,python中使用一个轮询机制,所有的控制都由这个调度器说了算,它会按照一定的机制反复查询,然后做出响应。
异步可以说是一系列的异步函数,至于什么是异步函数,其中细节就不深究,按照现在的标准,只要用async def
声明的函数就是一个异步函数,然后在异步函数中一般要有其他的异步操作,这个时候要使用await
语句来等待,看一看如何使用简单的异步函数。
""" 简单异步操作
异步过程
1.定义异步函数:async和await结合使用
2.获得轮询器:loop = asyncio.get_event_loop()
3.收集要异步的函数:tasks = [func1(), func2(), ...]
4.完成异步操作:loop.run_until_complete(asyncio.wait(tasks))
或者loop.run_until_complete(asyncio.gather(*tasks))
5.适当时候可以得到异步函数的返回值
notice:wait得到的结果是乱序的,gather得到的结果是有序的
the statistics of this file:
lines(count) understand_level(h/m/l) classes(count) functions(count) fields(count)
000000000100 ---------------------- 00000000000000 0000000000000002 ~~~~~~~~~~~~~
"""
import time
import asyncio
__author__ = '与C同行'
async def wait_block_n_second(n, verbose):
print('进入wait_block_n_second函数')
time.sleep(n)
print(f'{verbose}离开wait_block_n_second函数')
async def wait_noblock_n_second(n, verbose):
print('进入wait_noblock_n_second函数')
await asyncio.sleep(n)
print(f'{verbose}离开wait_noblock_n_second函数')
return n+1
if __name__ == '__main__':
print(f'当前时间:{time.ctime()}')
print('异步函数里没有异步操作')
loop = asyncio.get_event_loop()
try:
tasks = []
t1 = wait_block_n_second(1, '1s之后')
t2 = wait_block_n_second(2, '2s之后')
tasks.append(t1)
tasks.append(t2)
time_start_1 = time.time()
loop.run_until_complete(asyncio.wait(tasks))
time_end_1 = time.time()
print(f'异步函数中没有异步操作用时:{time_end_1-time_start_1}s')
finally:
loop.close()
print('异步函数中有异步操作')
loop = asyncio.get_event_loop()
try:
tasks = []
t1 = wait_noblock_n_second(2, '1s之后')
t2 = wait_noblock_n_second(1, '2s之后')
t3 = wait_noblock_n_second(3, '3s之后')
t4 = wait_noblock_n_second(4, '4s之后')
t5 = wait_noblock_n_second(6, '6s之后')
t6 = wait_noblock_n_second(5, '5s之后')
tasks.extend([t1, t2, t3, t4, t5, t6])
time_start_1 = time.time()
futures, pending = loop.run_until_complete(asyncio.wait(tasks))
time_end_1 = time.time()
print(f'异步函数中有异步操作用时:{time_end_1 - time_start_1}s')
print('使用wait的到无序的结果')
print(futures)
print(pending)
coro_1 = futures.pop()
print(type(coro_1))
print(coro_1.result())
for i in range(5):
coro = futures.pop()
print(coro.result())
finally:
loop.close()
print('异步函数中有异步操作')
loop = asyncio.get_event_loop()
try:
tasks = []
t1 = wait_noblock_n_second(2, '1s之后')
t2 = wait_noblock_n_second(1, '2s之后')
t3 = wait_noblock_n_second(3, '3s之后')
t4 = wait_noblock_n_second(4, '4s之后')
t5 = wait_noblock_n_second(6, '6s之后')
t6 = wait_noblock_n_second(5, '5s之后')
tasks.extend([t1, t2, t3, t4, t5, t6])
time_start_1 = time.time()
results = loop.run_until_complete(asyncio.gather(*tasks))
time_end_1 = time.time()
print(f'异步函数中有异步操作用时:{time_end_1 - time_start_1}s')
print('使用gather可以顺序输出结果')
print(results)
finally:
loop.close()
还记得上面讲的轮询机制吧,在代码中就是loop = asyncio.get_event_loop()
这条语句,先获得一个事件循环器,然后使用loop.run_until_complete()
一直监视异步函数,直到异步函数运行完成。
先看一下异步函数中没有使用await
语句的等待操作:
if __name__ == '__main__':
print(f'当前时间:{time.ctime()}')
print('异步函数里没有异步操作')
loop = asyncio.get_event_loop()
try:
tasks = []
t1 = wait_block_n_second(1, '1s之后')
t2 = wait_block_n_second(2, '2s之后')
tasks.append(t1)
tasks.append(t2)
time_start_1 = time.time()
loop.run_until_complete(asyncio.wait(tasks))
time_end_1 = time.time()
print(f'异步函数中没有异步操作用时:{time_end_1-time_start_1}s')
finally:
loop.close()
结果如下:
可以看到这样的异步函数跟同步函数运行结果没什么区别,接下来看看使用await
语句的异步函数:
if __name__ == '__main__':
print(f'当前时间:{time.ctime()}')
print('异步函数中有异步操作')
loop = asyncio.get_event_loop()
try:
tasks = []
t1 = wait_noblock_n_second(2, '1s之后')
t2 = wait_noblock_n_second(1, '2s之后')
t3 = wait_noblock_n_second(3, '3s之后')
t4 = wait_noblock_n_second(4, '4s之后')
t5 = wait_noblock_n_second(6, '6s之后')
t6 = wait_noblock_n_second(5, '5s之后')
tasks.extend([t1, t2, t3, t4, t5, t6])
time_start_1 = time.time()
futures, pending = loop.run_until_complete(asyncio.wait(tasks))
time_end_1 = time.time()
print(f'异步函数中有异步操作用时:{time_end_1 - time_start_1}s')
print('使用wait的到无序的结果')
print(futures)
print(pending)
coro_1 = futures.pop()
print(type(coro_1))
print(coro_1.result())
for i in range(5):
coro = futures.pop()
print(coro.result())
finally:
loop.close()
看一下结果:
使用await
之后的异步操作将会立即执行,不会阻塞程序,同时asyncio.wait()
的异步函数返回值是乱序的,最后看一下asyncio.gather()
的异步函数返回值:
if __name__ == '__main__':
print(f'当前时间:{time.ctime()}')
print('异步函数中有异步操作')
loop = asyncio.get_event_loop()
try:
tasks = []
t1 = wait_noblock_n_second(2, '1s之后')
t2 = wait_noblock_n_second(1, '2s之后')
t3 = wait_noblock_n_second(3, '3s之后')
t4 = wait_noblock_n_second(4, '4s之后')
t5 = wait_noblock_n_second(6, '6s之后')
t6 = wait_noblock_n_second(5, '5s之后')
tasks.extend([t1, t2, t3, t4, t5, t6])
time_start_1 = time.time()
results = loop.run_until_complete(asyncio.gather(*tasks))
time_end_1 = time.time()
print(f'异步函数中有异步操作用时:{time_end_1 - time_start_1}s')
print('使用gather可以顺序输出结果')
print(results)
finally:
loop.close()
结果如下:
经过asyncio.gather()
的异步函数的结果是有序的。
有没有入门异步方法,可能一次看不太懂,那就多看几次,后面再讲一些异步方法的其他方面。
喜欢python的伙伴可以关注微信公众号“与C同行”,一起进步。