1. asyncio源码探索 - 协程运行

图中为源码的运行过程

asyncio的运行.png

整体框架图

  • 从图中可以看出协程对象的运行通过self.loop.call_soon执行,不断往self._ready队列添加任务


    asyncio流程图.png

列出一些比较难懂的点

1.函数执行

Handle 》 self._context.run(self._callback,*self._args)

  • self._context = contextvars.copy_context() #此处用了协程的上下文,保证每个协程的内容互不干扰

2. self._callback

  • self._callback实际上是Task类中的__step方法

3. _register_task(self)

  • 每创建一个任务对象都放在 weakref.WeakSet()弱引用中

图中涉及到的代码

1. create_task

base_events.py > BaseEventLoop(events.AbstractEventLoop)

def create_task(self, coro, *, name=None):
    """Schedule a coroutine object.

    Return a task object.
    """
    self._check_closed()
    if self._task_factory is None:
        task = tasks.Task(coro, loop=self, name=name)
        if task._source_traceback:
            del task._source_traceback[-1]
    else:
        task = self._task_factory(self, coro)
        tasks._set_task_name(task, name)

    return task

1. run_forever

base_events.py > BaseEventLoop(events.AbstractEventLoop)

def run_forever(self):
    """Run until stop() is called."""
    self._check_closed()
    self._check_running()
    self._set_coroutine_origin_tracking(self._debug)
    self._thread_id = threading.get_ident()

    old_agen_hooks = sys.get_asyncgen_hooks()
    sys.set_asyncgen_hooks(firstiter=self._asyncgen_firstiter_hook,
                           finalizer=self._asyncgen_finalizer_hook)
    try:
        events._set_running_loop(self)
        while True:
            self._run_once()
            if self._stopping:
                break
    finally:
        self._stopping = False
        self._thread_id = None
        events._set_running_loop(None)
        self._set_coroutine_origin_tracking(False)
        sys.set_asyncgen_hooks(*old_agen_hooks)

2. Task

tasks.py 》 Task

class Task(futures._PyFuture): 
    _log_destroy_pending = True

    def __init__(self, coro, *, loop=None, name=None):
        super().__init__(loop=loop)
        if self._source_traceback:
            del self._source_traceback[-1]
        if not coroutines.iscoroutine(coro):
            # raise after Future.__init__(), attrs are required for __del__
            # prevent logging for pending task in __del__
            self._log_destroy_pending = False
            raise TypeError(f"a coroutine was expected, got {coro!r}")

        if name is None:
            self._name = f'Task-{_task_name_counter()}'
        else:
            self._name = str(name)

        self._must_cancel = False
        self._fut_waiter = None
        self._coro = coro
        self._context = contextvars.copy_context()

        self._loop.call_soon(self.__step, context=self._context)
        _register_task(self)

3. call_soon

base_events.py > BaseEventLoop(events.AbstractEventLoop)

def call_soon(self, callback, *args, context=None):
    self._check_closed()
    if self._debug:
        self._check_thread()
        self._check_callback(callback, 'call_soon')
    handle = self._call_soon(callback, args, context)
    if handle._source_traceback:
        del handle._source_traceback[-1]
    return handle

3. _call_soon

base_events.py > BaseEventLoop(events.AbstractEventLoop)

def _call_soon(self, callback, args, context):
    handle = events.Handle(callback, args, self, context)
    if handle._source_traceback:
        del handle._source_traceback[-1]
    self._ready.append(handle)
    return handle

4._run_once

base_events.py > BaseEventLoop(events.AbstractEventLoop)

#计时器取消记时数:_timer_cancelled_count
#任务数:sched_count
#最小取消计时器句柄数:_MIN_CANCELLED_TIMER_HANDLES_FRACTION
#最小计划计时器句柄:_MIN_SCHEDULED_TIMER_HANDLES
#最大选择超时:MAXIMUM_SELECT_TIMEOUT = 24* 3600
def _run_once():
    sched_count = len(self._scheduled)  #任务数
    if 任务数 > 最小计划计时器句柄 and
        计时器取消计时数/任务数 > 最小取消计时器句柄数:
        new_scheduled = []
        for handle in self._scheduled:
            # 判断取消,则把任务设置为False
            if handle._cancelled:
                handle._scheduled = False
            else:
                new_schedled.append(handle)
        # 二叉堆排序
        heapq.heapify(new_scheduled)
        self._scheduled = new_scheduled
        self._timer_cancelled_count = 0
    else:
        #从队列头删除已取消的延迟呼叫
        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:
        when = self._scheduled[0]._when
        # self.time()  根据事件循环的时钟返回时间
        timeout = min(max(0, when - self.time()), MAXIMUM_SELECT_TIMEOUT)
    event_list = self._selector.select(timeout)
    #进程选择器事件
    self._process_event(event_list)
    
    # 处理已准备好的回调 "later"
    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)
    
    #这是唯一一个真正调用回调的地方。
    #其他地方都准备好了。
    #注意:我们运行所有当前计划的回调,但不运行任何
    #按回调计划的回调这次运行--
    #它们将在下次运行(在另一个I/O轮询之后)。
    #使用不使用锁的线程安全的习惯用法。
    
    ntodo = len(self._ready)
    for i in range(ntodo):
        handle = self._ready.popleft()
        if handle._cancelled:
            continue
        if self._debug:
            try:
                self._current_handle = handle
                t0 = self.time()
                handle._run()
                dt = self.time() -t0
                if dt >= self.slow_callback_duration:
                    logger.warging("执行句柄(handle)花费了(dt)秒")
            finally:
                self._current_handle = None
        else:
            handle._run()
    handle = None  #发生异常时需要中断周期。

4. _run

events.py 》 Handle

def _run(self):
    try:
        self._context.run(self._callback, *self._args)
    except (SystemExit, KeyboardInterrupt):
        raise
    except BaseException as exc:
        cb = format_helpers._format_callback_source(
            self._callback, self._args)
        msg = f'Exception in callback {cb}'
        context = {
            'message': msg,
            'exception': exc,
            'handle': self,
        }
        if self._source_traceback:
            context['source_traceback'] = self._source_traceback
        self._loop.call_exception_handler(context)
    self = None  # Needed to break cycles when an exception occurs.

4. __step

tasks.py 》 Task

def __step(self, exc=None):
    if self.done():
        raise exceptions.InvalidStateError(
            f'_step(): already done: {self!r}, {exc!r}')
    if self._must_cancel:
        if not isinstance(exc, exceptions.CancelledError):
            exc = exceptions.CancelledError()
        self._must_cancel = False
    coro = self._coro
    self._fut_waiter = None

    _enter_task(self._loop, self)
    # Call either coro.throw(exc) or coro.send(None).
    try:
        if exc is None:
            # We use the `send` method directly, because coroutines
            # don't have `__iter__` and `__next__` methods.
            result = coro.send(None)
        else:
            result = coro.throw(exc)
    except StopIteration as exc:
        if self._must_cancel:
            # Task is cancelled right before coro stops.
            self._must_cancel = False
            super().cancel()
        else:
            super().set_result(exc.value)
    except exceptions.CancelledError:
        super().cancel()  # I.e., Future.cancel(self).
    except (KeyboardInterrupt, SystemExit) as exc:
        super().set_exception(exc)
        raise
    except BaseException as exc:
        super().set_exception(exc)
    else:
        blocking = getattr(result, '_asyncio_future_blocking', None)
        if blocking is not None:
            # Yielded Future must come from Future.__iter__().
            if futures._get_loop(result) is not self._loop:
                new_exc = RuntimeError(
                    f'Task {self!r} got Future '
                    f'{result!r} attached to a different loop')
                self._loop.call_soon(
                    self.__step, new_exc, context=self._context)
            elif blocking:
                if result is self:
                    new_exc = RuntimeError(
                        f'Task cannot await on itself: {self!r}')
                    self._loop.call_soon(
                        self.__step, new_exc, context=self._context)
                else:
                    result._asyncio_future_blocking = False
                    result.add_done_callback(
                        self.__wakeup, context=self._context)
                    self._fut_waiter = result
                    if self._must_cancel:
                        if self._fut_waiter.cancel():
                            self._must_cancel = False
            else:
                new_exc = RuntimeError(
                    f'yield was used instead of yield from '
                    f'in task {self!r} with {result!r}')
                self._loop.call_soon(
                    self.__step, new_exc, context=self._context)

        elif result is None:
            # Bare yield relinquishes control for one event loop iteration.
            self._loop.call_soon(self.__step, context=self._context)
        elif inspect.isgenerator(result):
            # Yielding a generator is just wrong.
            new_exc = RuntimeError(
                f'yield was used instead of yield from for '
                f'generator in task {self!r} with {result!r}')
            self._loop.call_soon(
                self.__step, new_exc, context=self._context)
        else:
            # Yielding something else is an error.
            new_exc = RuntimeError(f'Task got bad yield: {result!r}')
            self._loop.call_soon(
                self.__step, new_exc, context=self._context)
    finally:
        _leave_task(self._loop, self)
        self = None  # Needed to break cycles when an exception occurs.

4. add_done_callback

futures.py 》 Future

def add_done_callback(self, fn, *, context=None):
    """添加将来完成时要运行的回调。回调是用一个参数(未来对象)调用的。
      如果当调用此函数时,回调函数是很快就会打电话给你。
    """
    if self._state != _PENDING:
        self._loop.call_soon(fn, self, context=context)
    else:
        if context is None:
            context = contextvars.copy_context()
        self._callbacks.append((fn, context))

4. set_result

futures.py 》 Future

def set_result(self, result):
    """Mark the future done and set its result.

    If the future is already done when this method is called, raises
    InvalidStateError.
    """
    if self._state != _PENDING:
        raise exceptions.InvalidStateError(f'{self._state}: {self!r}')
    self._result = result
    self._state = _FINISHED
    self.__schedule_callbacks()

4. __schedule_callbacks

futures.py 》 Future

def __schedule_callbacks(self):
    """内部:要求事件循环调用所有回调
    """
    callbacks = self._callbacks[:]
    if not callbacks:
        return

    self._callbacks[:] = []
    for callback, ctx in callbacks:
        self._loop.call_soon(callback, self, context=ctx)

你可能感兴趣的:(1. asyncio源码探索 - 协程运行)