Рубрики
Без рубрики

Асинхронизация в Python с асинсио

Практическое руководство по модулю Python Asyncio. Tagged с Python, Asyncio.

Для людей, приезжающих из 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”