Twisted: сделать код неблокирующим - PullRequest
19 голосов
/ 25 мая 2011

Я немного озадачен тем, как писать асинхронный код на python / twisted.Предположим (ради аргумента), что я выставляю функцию миру, который возьмет число и вернет True / False, если это простое / не простое число, поэтому оно выглядит примерно так:


def IsPrime(numberin):
    for n in range(2,numberin):
        if numberin % n == 0: return(False)
    return(True)

(просто для иллюстрации).

Теперь предположим, что существует веб-сервер, который должен вызывать IsPrime на основе переданного значения.Это займет много времени для больших numberin.

Если в то же время другой пользователь запрашивает простоту небольшого числа, есть ли способ выполнить два вызова функции асинхронно, используя архитектуру реактора / отсрочки, чтобы результат короткого вычисления возвращался до того, какрезультат длинного калька?

Я понимаю, как это сделать, если функциональность IsPrime пришла от какого-либо другого веб-сервера, на который мой веб-сервер будет выполнять отложенную функцию getPage, но что, если это просто локальная функция?

, т. Е. Может ли Twistedкаким-то образом разделить время между двумя вызовами IsPrime, или это потребует явного вызова нового потока?

Или нужно ли разбивать цикл IsPrime на несколько более мелких циклов, чтобы можно было быстро передать управление реактору?

Или что-то еще?

1 Ответ

26 голосов
/ 25 мая 2011

Я думаю, что ваше текущее понимание в основном правильно. Twisted - это всего лишь библиотека Python, и код Python, который вы пишете для его использования, выполняется нормально, как вы ожидаете, что код Python: если у вас есть только один поток (и один процесс), то одновременно происходит только одно. API-интерфейсы, предоставляемые Twisted, практически не создают новые потоки или процессы, поэтому в обычном порядке ваш код выполняется последовательно; isPrime не может быть выполнен во второй раз, пока не завершится выполнение в первый раз.

Все еще рассматривая только один поток (и один процесс), весь «параллелизм» или «параллелизм» Twisted исходит из того факта, что вместо выполнения сетевого ввода-вывода (и некоторых других операций блокировки) Twisted предоставляет инструменты для выполнения операции неблокирующим способом. Это позволяет вашей программе продолжать выполнять другую работу, когда она могла бы застрять, ничего не делая, ожидая завершения блокирующей операции ввода-вывода (такой как чтение из или запись в сокет).

Можно сделать вещи "асинхронными", разбив их на маленькие порции и позволяя обработчикам событий запускаться между этими порциями. Иногда это полезный подход, если преобразование не делает код слишком сложным для понимания и сопровождения. Twisted предоставляет помощника для планирования этих кусков работы, cooperate. Полезно использовать этот помощник, поскольку он может принимать решения по планированию на основе всех различных источников работы и гарантировать, что у источников событий событий остается время без значительной дополнительной задержки (другими словами, чем больше заданий вы добавляете к нему тем меньше времени уходит на каждую работу, чтобы реактор продолжал выполнять свою работу.

Twisted также предоставляет несколько API для работы с потоками и процессами. Это может быть полезно, если не очевидно, как разбить работу на куски. Вы можете использовать deferToThread для запуска (поточно-ориентированной!) Функции в пуле потоков. Удобно, этот API возвращает Deferred, который в конечном итоге сработает с возвращаемым значением функции (или с Failure, если функция вызывает исключение). Эти Deferred'ы выглядят как любые другие, и с точки зрения кода, использующего их, он может также вернуться к вызову, подобному getPage - функции, которая не использует никаких дополнительных потоков, просто не блокировка ввода-вывода и обработчиков событий.

Поскольку Python не идеально подходит для запуска нескольких связанных с процессором потоков в одном процессе, Twisted также предоставляет неблокирующий API для запуска и взаимодействия с дочерними процессами. Вы можете перенести вычисления в такие процессы, чтобы воспользоваться преимуществами дополнительных процессоров или ядер, не беспокоясь о том, что GIL замедляет работу, чего не предлагают ни стратегия разбиения на блоки, ни подход потоков. API самого низкого уровня для работы с такими процессами - reactor.spawnProcess. Существует также Ampoule , пакет, который будет управлять пулом процессов для вас и предоставляет аналог deferToThread для процессов, deferToAMPProcess.

...