Для людей, приезжающих из JavaScript Асинхронное программирование, не являются чем -то новым, но для разработчиков Python привыкнуть к асинхронным функциям и будущему (эквивалент обещания в JS) не может быть тривиальным
Параллелизм и параллелизм могут показаться действительно похожими, но при программировании существует важная разница. Immagine, вы пишете книгу во время приготовления пищи, даже если кажется, что вы выполняете обе задачи одновременно, то, что вы делаете, переключается между двумя задачами, в то время как вы ждете кипения воды, вы пишете свою книгу, но но Пока вы нарезаете некоторые овощи, вы останавливаетесь на своем написании. Это называется параллелизм Анкет Единственный способ выполнить эти две задачи в параллель Имеет два человека, один надпись и одна кулинария, что делает многокайрный процессор.
Async Programming позволяет писать одновременный код, который запускается в одном потоке. Первое преимущество по сравнению с несколькими потоками заключается в том, что вы решаете, где планировщик переключится с одной задачи на другую, что означает, что обмен данными между задачами это безопаснее и проще.
def queue_push_back(x): if len(list) < max_size: list.append(x)
Если мы запустим код выше в многопоточной программе, возможно, что два потока выполняют строку 2 одновременно, поэтому в очередь будет добавлено 2 элемента одновременно и потенциально увеличивает размер очереди, чем max_size
Еще одним преимуществом асинхрового программирования является использование памяти. Каждый раз, когда создается новая поток, некоторая память используется для разрешения переключения контекста, если мы используем асинхронное программирование, это не является проблемой, поскольку код запускается в одном потоке.
Asyncio имеет 3 основных компонента: Coroutines, Loop Event и Future
Корутика
Коратика является результатом асинхронной функции, которая может быть объявлена с помощью ключевого слова Асинхронизация
до дефект
async def my_task(args): pass my_coroutine = my_task(args)
Когда мы объявляем функцию, используя Асинхронизация
Ключевое слово Функция не выполняется, вместо этого возвращается объект Coroutine.
Есть два способа прочитать вывод асинхронной функции из коратики. Первый способ – использовать Ждите
Ключевое слово, это возможно только внутри асинхронных функций и будет ждать, пока коратика завершит и вернет результат
result = await my_task(args)
Второй способ – добавить его в цикл событий, как мы увидим в следующих разделах.
Петля мероприятия
Цикл события – это объект, который выполняет наш асинхронный код и решает, как переключаться между асинхронными функциями. После создания цикла событий мы можем добавить в него несколько кругов, все эти коритины будут работать одновременно, когда run_until_complete
или run_forever
называется.
# create loop loop = asyncio.new_event_loop() # add coroutine to the loop future = loop.create_task(my_coroutine) # stop the program and execute all coroutine added # to the loop concurrently loop.run_until_complete(future) loop.close()
Будущее
Будущее – это объект, который работает в качестве заполнителя для вывода асинхронной функции, и дает нам информацию о состоянии функции. Будущее создается, когда мы добавляем цикл в цикл событий. Есть два пути к этому:
future1 = loop.create_task(my_coroutine) # or future2 = asyncio.ensure_future(my_coroutine)
Первый метод добавляет цинку в петлю и возвращает Задача
который является подтипом будущего. Второй метод очень похож, он требует коратики, и он добавляет его в цикл по умолчанию, единственное отличие состоит в том, что он также может принять будущее, и в этом случае он ничего не сделает и не вернет будущее без изменений.
Простая программа
import asyncio async def my_task(args): pass def main(): loop = asyncio.new_event_loop() coroutine1 = my_task() coroutine2 = my_task() task1 = loop.create_task(coroutine1) task2 = loop.create_task(coroutine2) loop.run_until_complete(asyncio.wait([task1, task2])) print('task1 result:', task1.result()) print('task2 result:', task2.result()) loop.close()
Как вы можете видеть, чтобы запустить асинхронную функцию, нам сначала необходимо создать кораку, затем мы добавляем ее в цикл события, который создает будущее/задачу. До этого момента ни один из кодов внутри нашей асинхронной функции не был выполнен, только когда мы называем loop.run_until_completed
Цикл событий начинает выполнять все коратики, которые были добавлены в цикл с loop.create_task
или asyncio.ensure_future
. loop.run_until_completed
Заблокирует вашу программу до тех пор, пока будущее, которое вы дали в качестве аргумента, не будет завершено. В примере мы использовали asyncio.wait ()
Чтобы создать будущее, которое будет завершено только тогда, когда все будущее, проходящее в списке аргументов, будет завершено.
Одна вещь, которую нужно помнить, когда писать асинхронные функции в Python, это то, что вы использовали Асинхронизация
до дефект
Это не значит, что ваша функция будет выполнена одновременно. Если вы выполняете нормальную функцию и добавите Асинхронизация
Перед ним цикл событий будет выполнять вашу функцию без перерыва, потому что вы не указывали, где цикл разрешена прерывать вашу функцию, чтобы запустить другую коратину. Укажите, где цикл событий разрешено изменять Coroutine, действительно прост, каждый раз, когда вы используете ключевое слово, ожидая, что цикл событий может прекратить выполнять вашу функцию и запустить еще одну Coroutine, зарегистрированную в цикле.
async def print_numbers_async1(n, prefix): for i in range(n): print(prefix, i) async def print_numbers_async2(n, prefix): for i in range(n): print(prefix, i) if i % 5 == 0: await asyncio.sleep(0) loop1 = asyncio.new_event_loop() count1_1 = loop1.create_task(print_numbers_async1(10, 'c1_1') count2_1 = loop1.create_task(print_numbers_async1(10, 'c2_1') loop1.run_until_complete(asyncio.wait([count1_1, count2_1]) loop1.close() loop2 = asyncio.new_event_loop() count1_2 = loop1.create_task(print_numbers_async1(10, 'c1_2') count2_2 = loop1.create_task(print_numbers_async1(10, 'c2_2') loop2.run_until_complete(asyncio.wait([count1_2, count2_2]) loop2.close()
Если мы выполним этот код, мы увидим, что Loop1 будет распечатать все номера с префиксом C1_1
а затем с префиксом C2_1
В то время как во втором цикле каждые 5 чисел цикл изменит задачу.
Теперь, когда мы знаем основы асинхронного программирования в Python, давайте напишем более реалистичный код, который загрузит список страниц из Интернета и распечатает предварительный просмотр, содержащий первые 3 строки страницы.
import aiohttp import asyncio async def print_preview(url): # connect to the server async with aiohttp.ClientSession() as session: # create get request async with session.get(url) as response: # wait for response response = await response.text() # print first 3 not empty lines count = 0 lines = list(filter(lambda x: len(x) > 0, response.split('\n'))) print('-'*80) for line in lines[:3]: print(line) print() def print_all_pages(): pages = [ 'http://textfiles.com/adventure/amforever.txt', 'http://textfiles.com/adventure/ballyhoo.txt', 'http://textfiles.com/adventure/bardstale.txt', ] tasks = [] loop = asyncio.new_event_loop() for page in pages: tasks.append(loop.create_task(print_preview(page))) loop.run_until_complete(asyncio.wait(tasks)) loop.close()
Этот код должен быть довольно простым для понимания, мы начинаем с создания асинхронной функции, которая загружает URL и печатает первые 3, а не пустые строки. Затем мы создаем функцию, которая для каждой страницы в списке вызова страниц print_preview
, Добавьте Coroutine To Top и храните будущее в списке задач. Наконец, мы запускаем цикл событий, который запустит конутину, которую мы добавили к нему, и он печатает предварительный просмотр всех страниц.
Последняя функция, о которой я хочу поговорить, – это асинхронный генератор. Реализация асинхронного генератора довольно проста.
import asyncio import math import random async def is_prime(n): if n < 2: return True for i in range(2, n): # allow event_loop to run other coroutine await asyncio.sleep(0) if n % i == 0: return False return True async def prime_generator(n_prime): counter = 0 n = 0 while counter < n_prime: n += 1 # wait for is_prime to finish prime = await is_prime(n) if prime: yield n counter += 1 async def check_email(limit): for i in range(limit): if random.random() > 0.8: print('1 new email') else: print('0 new email') await asyncio.sleep(2) async def print_prime(n): async for prime in prime_generator(n): print('new prime number found:', prime) def main(): loop = asyncio.new_event_loop() prime = loop.create_task(print_prime(3000)) email = loop.create_task(check_email(10)) loop.run_until_complete(asyncio.wait([prime, email])) loop.close()
Когда в коратине поднимается нездоровое исключение, оно не нарушает нашу программу, как в обычном синхронном программировании, вместо этого оно хранится в будущем, и если вы не обработаете исключение до выхода программы, вы получите следующую ошибку
Task exception was never retrieved
Есть два способа исправить это, поймайте исключение, когда вы получаете доступ к будущему результату или вызовут метод будущего исключения.
try: # this will raise the exception raised during the coroutine execution my_promise.result() catch Exception: pass # this will return the exception raised during the coroutine execution my_promise.exception()
Если вы прочитали все до этого момента, вы должны знать, как использовать Asyncio для написания одновременного кода, но если вы хотите пойти глубже и понять, как работает Asyncio, я предлагаю вам посмотреть следующее видео
Если вы хотите увидеть более сложное использование Asyncio или если у вас есть какой -либо вопрос, оставьте комментарий, и я воспроизводим вам как можно скорее
Оригинал: “https://dev.to/welldone2094/async-programming-in-python-with-asyncio-12dl”