python3-asyncio 学习笔记 2 -- call_at

异步函数调用方式,除了call_soon(call_soon_threadsafe)还有一种延迟调用,即call_later,call_at.通过查看源码,会轻易的发现, call_later 其实是通过call_at实现的.

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

所以只需要研究 call_at 就可以了.

def call_at(self, when, callback, *args):
    self._check_closed()
    if self._debug:
        self._check_thread()
        self._check_callback(callback, 'call_at')
    timer = events.TimerHandle(when, callback, args, self)
    if timer._source_traceback:
        del timer._source_traceback[-1]
    heapq.heappush(self._scheduled, timer)
    timer._scheduled = True
    return timer

可以看出,在 call_at 内部实际是构建了一个和之前call_soon里看到的 Handle 类似的events.TimerHandle.

而这个events.TimerHandle 与之前的Handle 在于它重载(或许重写比较合适)了大小比较,即Handle 之间可以比较,比较的依据就是它理应(注意此处的理应)执行的时间的时间戳.所以之后的:

heapq.heappush(self._scheduled, timer)

就有了比较的依据,由于python中的优先队列是 min heap .即大小顺序是又小到大,所以时间戳小的在前,及理应早些发生的在前面. 虽然这里构造了TimerHandle 实例,但是并没有真正的执行, 真正的执行还是在call_soon 里提到的_run_once中. 

sched_count = len(self._scheduled)
if (sched_count > _MIN_SCHEDULED_TIMER_HANDLES and
    self._timer_cancelled_count / sched_count >
        _MIN_CANCELLED_TIMER_HANDLES_FRACTION):
        # Remove delayed calls that were cancelled if their number
        # is too high
    new_scheduled = []
    for handle in self._scheduled:
        if handle._cancelled:
            handle._scheduled = False
        else:
            new_scheduled.append(handle)

    heapq.heapify(new_scheduled)
    self._scheduled = new_scheduled
    self._timer_cancelled_count = 0
else:
    # Remove delayed calls that were cancelled from head of queue.
    while self._scheduled and self._scheduled[0]._cancelled:
        self._timer_cancelled_count -= 1
        handle = heapq.heappop(self._scheduled)
        handle._scheduled = False

先是将已经取消的任务去掉.然后根据最先要执行的任务的时间,决定等待多长时间.

timeout = None
if self._ready or self._stopping:
    timeout = 0
elif self._scheduled:
    # Compute the desired timeout.
    when = self._scheduled[0]._when
    timeout = max(0, when - self.time())
event_list = self._selector.select(timeout)
self._process_events(event_list)

然后计算并比较当前时间和任务的理论执行时间,决定哪些任务将要执行

# Handle 'later' callbacks that are ready.
end_time = self.time() + self._clock_resolution
while self._scheduled:
    handle = self._scheduled[0]
    if handle._when >= end_time:
        break
    handle = heapq.heappop(self._scheduled)
    handle._scheduled = False
    self._ready.append(handle)

可以看到和call_soon一样,都是添加到self._ready这个队列中,依照FIFO原则执行.

在结尾处我们探讨一下为什么之前说 理应执行时间 ?

其实由最终call_at添加的计划任务转到self._ready这个队列中就可以简单的推测出,如果在延迟的任务前,有一个特别耗时的操作,后面的任务即便到了理论时间还是需要等待当前任务完成的.下面给出一个不太科学的例子,可以自己体会一下:

#!/usr/bin/python3
#-*-coding:utf8-*-
'''
学习asyncio 之 call_later, 
观察 call_later 到底什么时候执行的.
'''
import time
import asyncio
import requests


def block_loop():
    print("In block")
    print(time.time())
    try:
        requests.get("https://www.google.com")#一个延迟高甚至不可达的地址即可
    except:
        pass

def hello_world(loop):
    print(time.time())
    print('Hello World')
    loop.stop()

loop = asyncio.get_event_loop()
loop.set_debug(True)
print(time.time())
# Schedule a call to hello_world()
loop.call_soon(block_loop)
print("call soon")
print(time.time())
loop.call_later(2, hello_world, loop)
print("Register call later.")
print(time.time())
# Blocking call interrupted by loop.stop()
print("Start loop")
loop.run_forever()
loop.close()

 

转载于:https://my.oschina.net/chinesezhx/blog/867424

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值