Как я могу предотвратить переключение контекста при вызове асинхронной функции? - PullRequest
0 голосов
/ 25 февраля 2019

Если я использую асинхронные функции, то все функции над стеком также должны быть асинхронными, и их вызову должно предшествовать ключевое слово await.В этом примере эмулируются современные программы с несколькими архитектурными уровнями приложения:

async def func1():
    await asyncio.sleep(1)

async def func2():
    await func1()

async def func3():
    await func2()

async def func4():
    await func3()

async def func5():
    await func4()

Когда поток выполнения встречает «ожидание», он может переключиться на другую сопрограмму, которая требует ресурсов для переключения контекста.Из-за большого количества конкурирующих правил и различных уровней абстракции эти издержки могут начать ограничивать производительность всей системы.Но в представленном примере имеет смысл переключать контекст только в одном случае:

await asyncio.sleep(1)

Как можно запретить переключение контекста для определенных асинхронных функций?

1 Ответ

0 голосов
/ 25 февраля 2019

Прежде всего, по умолчанию в вашем примере контекст не будет переключаться.Другими словами, пока сопрограмма не столкнется с чем-то, что фактически блокирует (например, Future), она не вернет управление циклу событий и не возобновит свой путь непосредственно к внутренней сопрограмме.

Я не знаю более простого способа продемонстрироватьэто чем наследование реализации цикла событий по умолчанию:

import asyncio


class TestEventLoop(asyncio.SelectorEventLoop):
    def _run_once(self):
        print('control inside event loop')
        super()._run_once()


async def func1():
    await asyncio.sleep(1)


async def func2():
    print('before func1')
    await func1()
    print('after func1')


async def main():
    print('before func2')
    await func2()
    print('after func2')


loop = TestEventLoop()
asyncio.set_event_loop(loop)
try:
    loop.run_until_complete(main())
finally:
    loop.close()

В выводе вы увидите:

control inside event loop
before func2
before func1
control inside event loop
control inside event loop
after func1
after func2
control inside event loop

func2 передал поток выполнения непосредственно в func1, избегая _run_once цикла обработкиэто может переключиться на другую сопрограмму.Только при блокировке asyncio.sleep цикл событий получил контроль.

Хотя это деталь реализации цикла событий по умолчанию.


Во-вторых, и это, вероятно,Гораздо важнее, что переключение между сопрограммами обходится очень дешево по сравнению с тем преимуществом, которое мы получаем от использования asyncio для работы с вводом / выводом.

Это также намного дешевле, чем другие альтернативы асинхронности, такие как переключение между потоками ОС.

Ситуация, когда ваш код работает медленно из-за множества сопрограмм, крайне маловероятна, но даже если это произошло, вам, вероятно, стоит взглянуть на более эффективные реализации цикла событий, такие как uvloop .

...