13 asyncio模块
13.1 事件循环
-
大多数异步应用程序实现异步的基本机制 通过在后台执行的事件循环,当代码需要被执行时,才会注册到循环中。 把一个函数注册到事件循环会导致它变成一个任务,事件循环负责在获得任务后,马上执行它。另一种方式是事件循环有时在等待一定时间后,再执行任务。
一个简单的事件循环
import asyncio loop = asyncio.get_event_loop() print(loop.is_running()) # False # 执行循环 # loop.run_forever() # 注册任务并执行循环 import functools def hello_world(): print('hello world!') def stop_loop(loop): print('stopping loop!') loop.stop() loop.call_soon(hello_world) # partial 偏函数 loop.call_soon(functools.partial(stop_loop, loop)) loop.run_forever()
# 延迟调用 单位秒 loop.call_later(10, hello_world) loop.call_later(12, functools.partial(stop_loop, loop)) loop.run_forever()
-
大多数接受函数的asyncio 方法仅仅接受函数对象(或其他被调用元素)但这些函数在被调用时并没有带参数。partial方法本身接受参数与关键字参数,这些参数在底层函数被调用时被传给底层函数。
def hello_world(): print(" Hello world!") ## 等价于 functools.partial(print, 'Hello world!')
-
-
任务结束之前,一直执行循环
@asyncio.coroutine
将普通的Python函数转换成一个协程,当调用run_until_complete时,会将任务注册并在任务结束前执行循环
@asyncio.coroutine def trivial(): return 'Hello world!' loop.run_until_complete(trivial())
-
执行一个后台循环
import asyncio import threading import functools def run_loop_forever_in_background(loop): def thread_func(l): asyncio.set_event_loop(l) l.run_forever() thread = threading.Thread(target=thread_func, args=(loop, )) thread.start() return thread loop = asyncio.get_event_loop() run_loop_forever_in_background(loop) loop.call_soon_threadsafe(functools.partial(print, 'Hello world!'))
13.2 协程
-
协程是一种用于在事件循环中执行的特殊函数;如果创建了协程并未执行它,日志记录中会出错
import asyncio @asyncio.coroutine def coro_sum(*args): answer = 0 for i in args: answer += i return answer loop = asyncio.get_event_loop() print(loop.run_until_complete(coro_sum(1,2,3,4,5)))
-
嵌套
-
outer的协程使用yield from语法调用名称 nestedde 协程;outer的协程遇到yield from语句是会挂起。nested协程被放入事件循环并执行,outer协程在nested完成并返回结果之前不会继续执行。
import asyncio @asyncio.coroutine def nested(*args): print('The `nested` function ran with args: %r'%(args,)) return [i+1 for i in args] @asyncio.coroutine def outer(*args): print('The `outer` function ran with args: %r'%(args,)) answer = yield from nested(*[i*2 for i in args]) return answer loop = asyncio.get_event_loop() print(loop.run_until_complete(outer(2, 3, 5, 8))) '''''' The `outer` function ran with args: (2, 3, 5, 8) The `nested` function ran with args: (4, 6, 10, 16) [5, 7, 11, 17]
-
13.3 Future对象和 Task对象
-
Future对象: 用于通知异步函数状态的对象,包括函数的 执行、 完成、 取消、函数的结果或者函数引发异常,返回对应的异常和回溯。
-
Task对象是 Future对象的子类,每一个协程在事件循环中被安排执行后,协程就会被Task对象包装。当你调用
run_until_complete
并传递一个协程时,该协程会被包装到一个Task对象中并执行。Task对象的任务是存储结果并为``yield from语句 提供值。 -
asyncio.async
将协程放入事件循环,并返回对应的Task对象。import asyncio @asyncio.coroutine async def make_tea(variety): print("Now making %s tea."%variety) asyncio.get_event_loop().stop() # return '%s tea'%variety loop = asyncio.get_event_loop() # 注册事件循环 task = loop.create_task(make_tea('chamomile')) loop.run_forever() #Now making chamomile tea. print(task.done()) #True print(task.result()) #chamomile tea task = [loop.create_task(make_tea('chamomile'))] loop.run_until_complete(asyncio.wait(task)) print(task[0].done()) #True print(task[0].result()) #chamomile tea
-
回调
import asyncio @asyncio.coroutine async def make_tea(variety): print("Now making %s tea."%variety) return '%s tea'%variety def confirm_tea(future): print('The %s is made.'%future.result()) loop = asyncio.get_event_loop() # 注册事件循环 task = loop.create_task(make_tea('green')) # 将confirm_tea方法作为回调赋值给task的地方;相当于调用 confirme_tea(task)? task.add_done_callback(confirm_tea) loop.run_until_complete(task)
-
带参数的回调
import asyncio import functools @asyncio.coroutine async def make_tea(variety): print("Now making %s tea."%variety) return '%s tea'%variety def confirm_tea(ingredient, future): print('Now adding %s to the %s.'%(ingredient, future.result())) loop = asyncio.get_event_loop() # 注册事件循环 task = loop.create_task(make_tea('green')) # 增加偏函数 设定默认值 task.add_done_callback(functools.partial(confirm_tea, 'honey')) loop.run_until_complete(task)
13.5 任务聚合
-
聚集任务:聚集任务目的提供的第一种机制是通过 gather函数;gather函数接受一系列协程或任务,并返回将那些任务聚合后的单个任务
import asyncio loop = asyncio.get_event_loop() @asyncio.coroutine def make_tea(variety): print("Now making %s tea."%variety) return '%s tea'%variety def mix(future): print("Mixing the %s together."%' and '.join(future.result())) meta_task = asyncio.gather( make_tea('chamomile'), make_tea('green'), make_tea('herbal') ) meta_task.add_done_callback(mix) loop.run_until_complete(meta_task)
-
等待任务:
asyncio.wait
协程接受一系列协程或任务,一旦完成就返回结果。注意该协程的签名与asyncio.gather不同。 每个协程或任务都是gather的一个单独位置参数,而wait接受一个列表作为参数。此外,wait接受一个用于在任何任务完成后返回的参数,而无需等待所有任务完成。wait返回两部分:第一个元素是已完成的Future对象,第二个元素为未完成的部分。import asyncio loop = asyncio.get_event_loop() @asyncio.coroutine def make_tea(variety): print("Now making %s tea." % variety) return '%s tea'%variety coro = asyncio.wait([ make_tea('chamomile'), make_tea('green'), make_tea('herbal')]) loop.run_until_complete(coro)
-
超时
import time import asyncio loop = asyncio.get_event_loop() coro = asyncio.wait([ asyncio.sleep(5), asyncio.sleep(3)], timeout=3 ) t1 = time.time() print(loop.run_until_complete(coro)) t2 = time.time() print('time:%s'%(t2-t1))
-
等待任意任务
import time import asyncio loop = asyncio.get_event_loop() coro = asyncio.wait([ asyncio.sleep(5), asyncio.sleep(3)], return_when=asyncio.FIRST_COMPLETED # 等待任意任务完成 ) t1 = time.time() loop.run_until_complete(coro) t2 = time.time() print('time:%s'%(t2-t1))
-
等待异常
import asyncio loop = asyncio.get_event_loop() @asyncio.coroutine def raise_ex_after(seconds): yield from asyncio.sleep(seconds) raise RuntimeError('Raising an exception.') coro = asyncio.wait( [asyncio.sleep(1), raise_ex_after(2), asyncio.sleep(3)], return_when=asyncio.FIRST_EXCEPTION # 等待抛出异常结束 ) t1 = time.time() loop.run_until_complete(coro) t2 = time.time() print('time:%s'%(t2-t1))
-
-
队列
-
全功能队列应用库
celery
-
asyncio 模块提供队列仅仅是基本的队列; Queue类提供的方法被用于顺序或一部上下文中
-
非常直接的先进先出(FIFO)队列;put_nowait和get_nowait 添加或移除项;
import asyncio queue = asyncio.Queue() queue.put_nowait('foo') print(queue.qsize()) # 1 print(queue.get_nowait()) # foo print(queue.qsize()) # 0
-
Queue类 提供一个名称为 get的方法,get方法在队列为空时不会引发异常,而是等待 项 被添加到队列中, 然后再从队列中获得该项并立刻返回。
import asyncio loop = asyncio.get_event_loop() queue = asyncio.Queue() queue.put_nowait('foo') print(loop.run_until_complete(queue.get()))
import asyncio loop = asyncio.get_event_loop() queue = asyncio.Queue(maxsize=5) # 设置最大队列长度 task = loop.create_task(queue.get()) coro = asyncio.wait([task], timeout=1) print(loop.run_until_complete(coro)) # 因为队列为空,处于阻塞状态 print(task.done()) # False queue.put_nowait('bar') import functools def stop(l, future): l.stop() task.add_done_callback(functools.partial(stop, loop)) loop.run_forever() print(task.done()) # True print(task.result()) # bar
-
-
服务器
-
asyncio 模块最常见的用处是创建一个座位守护程序的服务并接受命令。asyncio模块 定义了一个Protocol类,用于在接受或失去连接时以接受到数据时触发对应事件。 此外,事件循环定义了一个create_server 方法,用于打开一个 socket连接,该连接允许将数据发送到事件循环并交给Protocol类。
-
''' 在命令行执行: telnet 127.0.0.1 8000 ''' import asyncio from asyncio import transports class Shutdown(Exception): pass class ServerProtocol(asyncio.Protocol): def connection_made(self, transport: transports.BaseTransport) -> None: self.transport = transport self.write('Welcome\r') self.data = '' # 用于接收 指令 def data_received(self, data) -> None: # print(data) if not data: return elif data == b'\r\n': message = self.data command = message.strip().split(' ')[0].lower() # 只是以一个空格符作为分隔符 args = message.strip().split(' ')[1:] # print(command) if not hasattr(self, 'command_%s'%command): self.write('\rInvalid command:%s \n\r'%command) self.data = '' return try: self.data = '' return getattr(self, 'command_%s'%command)(*args) except Exception as ex: self.write('Error: %s\r'%str(ex)) self.data = '' else: self.data += data.decode('ascii') def write(self, msg_string): msg_string += '\n' self.transport.write(msg_string.encode('ascii', 'ingore')) def command_add(self, *args): # print(args) args = [int(i) for i in args] self.write('%d\r'%sum(args)) def command_shutdown(self): raise KeyboardInterrupt if __name__ == '__main__': loop = asyncio.get_event_loop() coro = loop.create_server(ServerProtocol, '127.0.0.1', 8000) loop.create_task(coro) try: loop.run_forever() except KeyboardInterrupt: pass
-