python asyncio 相关整理

python asyncio 相关整理

SelectEventLoop问题

import asyncio

import aiohttp

if __name__ == '__main__':

    async def fetch(url):
        async with aiohttp.ClientSession() as session:
            async with session.get(url) as response:
                return await response.text()

    async def test(i):
        print(i)
        await fetch("https://baidu.com")
        pass

    tasks = []

    import selectors

    # 创建一个基于 SelectSelector 的事件循环
    selector = selectors.SelectSelector()

    loop = asyncio.SelectorEventLoop(selector=selector)

    for i in range(512):
        tasks.append(loop.create_task(test(i)))

    loop.run_until_complete(asyncio.gather(*tasks))

上面的代码基于python 3.6默认的事件循环 SelectEventLoop,会出现socket耗尽的情况。

如果使用ProactorEventLoop就没有问题。

Windows事件循环的一般限制:

特定于 SelectorEventLoop 的限制:

特定于 ProactorEventLoop 的限制:

注意到SelectorEventLoop只能使用套接字监控IO操作,最多是512个。

事件循环与协程

协程包含4个状态:挂起(Suspended)就绪(Ready)完成(Finished)运行中(Running)

事件循环只处理协程的挂起和就绪状态。

SelectorEventLoop通过selectors.SelectSelector 来监控 I/O 事件,从而判断协程是否可以继续执行。具体步骤如下:

  1. 注册 I/O 事件

当协程中使用 await 等待某个 I/O 操作(例如 await asyncio.sleep()await sock.recv())时,事件循环会将该协程挂起,并将对应的 I/O 操作注册到 SelectSelector 中。

  1. 监控 I/O 事件

SelectSelector 会调用 select.select() 系统调用,监控所有注册的 I/O 事件。select.select() 会阻塞,直到有 I/O 事件发生(例如套接字可读或可写)。

  1. 事件触发

当某个 I/O 事件发生时,SelectSelector 会返回对应的文件描述符。事件循环会根据返回的文件描述符,找到对应的协程,并将其标记为就绪。

  1. 执行就绪协程

事件循环会从就绪队列中取出协程,并继续执行。如果协程中还有其他 await 操作,会重复上述过程。

coroutine、task、future

coroutine可以调用coroutine,实现了一个函数暂停和恢复运行的功能

future用于表示一个同步函数或异步函数的运行结果,当函数还未完成时,获取future.result会抛出异常。

task是future的子类,它包装了协程,能返回协程的执行结果。

asyncio.sleep实现原理

# asyncio.sleep
@coroutine
def sleep(delay, result=None, *, loop=None):
    """Coroutine that completes after a given time (in seconds)."""
    # 暂停0秒,也就是当前协程主动交出控制权
    if delay == 0:
        yield
        return result

    if loop is None:
        loop = events.get_event_loop()

    future = loop.create_future()
    # 定时,delay秒后执行futures._set_result_unless_cancelled函数
    h = future._loop.call_later(delay,
                                futures._set_result_unless_cancelled,
                                future, result)
    try:
        return (yield from future)
    finally:
        h.cancel()
# loop.call_later
def call_later(self, delay, callback, *args):
    
    timer = self.call_at(self.time() + delay, callback, *args)
    if timer._source_traceback:
        del timer._source_traceback[-1]
    return timer
# loop.call_once
def call_at(self, when, callback, *args):
    self._check_closed()
    if self._debug:
        self._check_thread()
        self._check_callback(callback, 'call_at')
    # 创建一个timer,那么要去看这个timer怎么被调用
    timer = events.TimerHandle(when, callback, args, self)
    if timer._source_traceback:
        del timer._source_traceback[-1]
    # 将timer放入等待队列
    heapq.heappush(self._scheduled, timer)
    timer._scheduled = True
    return timer
# loop._run_once
def _run_once(self):
    # 设置selector获取就绪协程的等待时间
    timeout = None
    if self._ready or self._stopping:
        timeout = 0
    elif self._scheduled:
        # Compute the desired timeout.
        # 取堆头第一个协程,计算最短的就绪时间
        when = self._scheduled[0]._when
        timeout = min(max(0, when - self.time()), MAXIMUM_SELECT_TIMEOUT)
    if self._debug and timeout != 0:
        # 不关心
    	pass
    else:
        # selector获取就绪协程
        event_list = self._selector.select(timeout)
    # 运行就绪协程
    self._process_events(event_list)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值