Этот вопрос мотивирован моим другим вопросом: как ждать в cdef?
В Интернете есть много статей и сообщений в блоге об asyncio
, но все они очень поверхностны. Я не мог найти никакой информации о том, как фактически реализован asyncio
, и что делает асинхронным I/O. Я пытался прочитать исходный код, но это тысячи строк не самого высокого класса C-кода, многие из которых связаны с вспомогательными объектами, но наиболее важно, что трудно установить синтаксис Python и какой код C он переведет в.
Другая документация Asycnio еще менее полезна. Там нет информации о том, как это работает, только некоторые рекомендации о том, как ее использовать, которые также иногда вводят в заблуждение/очень плохо написаны.
Я знаком с Go-реализацией сопрограмм и очень надеюсь, что Python сделает то же самое. Если бы это было так, то код, который я нашел в указанной выше ссылке, сработал бы. Так как это не так, я теперь пытаюсь понять, почему. До сих пор я догадываюсь, пожалуйста, поправьте меня, где я ошибаюсь:
- Определения процедур формы
async def foo():...
фактически интерпретируются как методы класса, наследующегоcoroutine
. - Возможно,
async def
фактически разбит на несколько методов операторамиawait
, где объект, на который эти методы вызваны, способен отслеживать прогресс, достигнутый в ходе выполнения. - Если вышесказанное верно, то, по сути, выполнение сопрограммы сводится к вызовам методов объекта coroutine некоторым глобальным менеджером (loop?).
- Глобальный менеджер каким-то образом (как?) Знает, когда операции ввода-вывода выполняются с помощью кода Python (только?), И он может выбрать один из ожидающих методов coroutine для выполнения после того, как текущий метод выполнения отказался от управления (нажать на
await
выражение).
Другими словами, здесь моя попытка "desugaring" какого-либо синтаксиса asyncio
во что-то более понятное:
async def coro(name):
print('before', name)
await asyncio.sleep()
print('after', name)
asyncio.gather(coro('first'), coro('second'))
# translated from async def coro(name)
class Coro(coroutine):
def before(self, name):
print('before', name)
def after(self, name):
print('after', name)
def __init__(self, name):
self.name = name
self.parts = self.before, self.after
self.pos = 0
def __call__():
self.parts[self.pos](self.name)
self.pos += 1
def done(self):
return self.pos == len(self.parts)
# translated from asyncio.gather()
class AsyncIOManager:
def gather(*coros):
while not every(c.done() for c in coros):
coro = random.choice(coros)
coro()
Если мое предположение окажется правильным: тогда у меня есть проблема. Как действительно в действительности I/O происходит в этом сценарии? В отдельном потоке? Выключен ли весь интерпретатор, а ввод/вывод происходит за пределами интерпретатора? Что именно подразумевается под I/O? Если моя процедура python называется процедурой C open()
, и она, в свою очередь, отправляет прерывание в ядро, отказывается от управления им, как интерпретатор Python знает об этом и может продолжить запуск другого кода, в то время как код ядра делает фактический ввод /O и до тех пор, пока он не пробудет процедуру Python, которая первоначально отправила прерывание? Как Python может интерпретировать в принципе, знать об этом?