50ae12a099b01792 8 951 98 30 964 info@severcart.org
Русский English

Асинхронное программирования в Python. Часть 4

6 января 2018 г.

Пример 8: Асинхронные (неблокирующие) HTTP загрузки с использованием обратных вызовов Twisted

Предыдущие часть 1, часть 2, часть 3

Новая версия программы (example_8.py) также использует библиотеку Twisted, но иллюстрирует более традиционный подход использования Twisted.

Под этим подразумевается, отказ от использования стиля @defer.inlineCallbacks / yield для кодирования и использование явных обратных вызовов. Обратный вызов – это функция, которая регистрируется в системе и может быть вызвана позже в ответ на событие. В приведенном ниже примере поставляемая с Twisted функция success_callback() используется для вызова при завершения выполнения getPage(url).

Обратите внимание, что в программе больше не присутствует декоратор @defer.inlineCallbacks для функции my_task(). Кроме того, функция возвращает переменную d, получаемую вызовом функции getPage(url).

Отложенный Twisted метод предназначен для асинхронного программирования и является точкой привязки обратного вызова. Функция getPage(url) возвращает отложенный обратный вызов страницы (как строки) или errback с описанием ошибки.

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

"""
example_8.py

Загрузка содержимого по URL адресу, полученный из очереди. 
Для обеспечения параллелизма используется Twisted.
"""

from twisted.internet import defer
from twisted.web.client import getPage
from twisted.internet import reactor, task

import queue
from lib.elapsed_time import ET


def success_callback(results, name, url, et):
    print(f'Task {name} got URL: {url}')
    print(f'Task {name} total elapsed time: {et():.1f}')


def my_task(name, queue):
    if not queue.empty():
        while not queue.empty():
            url = queue.get()
            print(f'Task {name} getting URL: {url}')
            et = ET()
            d = getPage(url)
            d.addCallback(success_callback, name, url, et)
            yield d


def main():
    """
    main точка входа в программу
    """
    # создание очереди work_queue
    work_queue = queue.Queue()

    # помещаем url в очередь
    for url in [
        b"http://google.com",
        b"http://yahoo.com",
        b"http://linkedin.com",
        b"http://shutterfly.com",
        b"http://mypublisher.com",
        b"http://facebook.com"
    ]:
        work_queue.put(url)

    # запускаем задачи
    et = ET()

    # создаём кооператор
    coop = task.Cooperator()

    defer.DeferredList([
        coop.coiterate(my_task('One', work_queue)),
        coop.coiterate(my_task('Two', work_queue)),
    ]).addCallback(lambda _: reactor.stop())

    # запускаем event loop
    reactor.run()

    print()
    print(f'Total elapsed time: {et():.1f}')


if __name__ == '__main__':
    main()

Результат запуска этой программы такой же, как и в предыдущих двух примерах. Общее время программы меньше, чем время выполнения URL адресов.

Используете ли вы gevent или Twisted, это вопрос личного предпочтения и стиля кодирования. Оба являются мощными библиотеками, которые предоставляют механизмы, позволяющие программисту создавать асинхронный код.

Вывод

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

Однако, если Вы хотите реализовать сервер или программу, выполняющую значительное количество IO, то асинхронный код может оказать огромное подспорье. Это мощная технология, которая может улучшить Ваши программы до следующего уровня.