r1der
Май 6, 2010 02:06:37
Как выполняются асинхронные действия внутри?(не смог сам понять по исходнику), все ведь работает в одном потоке так?, каким тогда образом например под вин или юникс-лайк работает именно асинхронность? Пожалуйста объясните.. И какие еще есть способы реализации асинхронности?(не потоки, не процессы) Спасибо заранее)
Я не знаю, что такое deffered, это раз.
Deferred это просто система callback'ов, это два.
А какие могут быть непонятки с асинхронностью? Приходит пакет по сети, вызывается callback, выполняется код, вызывается ещё один callback (например, через reactor.callLater(0.0, defer.call, some_result)) и т.д. Всё делается в единственном потоке.
..bw
r1der
Май 6, 2010 15:34:41
на сокетах асинхронность достигается неблокируещими сокетами внутри или select().. а как выполнять асинхронно не сетевые операции? я так понимаю что это в twisted тоже возможно?
Evg
Май 10, 2010 12:40:20
d = threads.deferToThread(dummyfunc)
d.addCallback(OnCalcCallback)
Это очевидно, ответ должен быть сложнее, но я пока не закончил его формулировку :-).
..bw
Evg
Май 10, 2010 13:54:22
я только недавно начал смотреть на твистед, сложнее сказать не могу) Такой вопрос, в документации есть раздел как писать серверы:
http://twistedmatrix.com/documents/current/core/howto/servers.htmlВот если реализовывать такой простой сервер по документации, каким образом будет идти обработка запроса внутри твистед? Насколько я представляю это будет последовательная обработка запроса, те если один тяжелый запрос выполняется остальные маленькие ждут, те не распаралеливается обработка, тк там один поток работает.. верно ли это? и как можно тогда сделать их выполнение параллельно на твистед и можно ли?)
Андрей Светлов
Май 10, 2010 21:34:06
Во первых, кроме deferToThread есть еще процессы, последовательные порты и прочее.
Если handle можно переключить в non blocking mode - то можно и создать twisted transport для него. Без особыс проблем.
Да, в twisted работает один главный поток. Плюс один или несколько ThreadPool. Можно создать еще что-нибудь, но не предстваляю зачем.
Один тяжедый запрос разваливается на много маленьких deferred, между которыми вполне может проскочить несколько легковесных запросов.
Кстати, а для чего выполнять “параллельно”? И что означает “праллельно”?
Evg
Май 11, 2010 03:25:32
Паралельно я имею ввиду создавать например поток для вычисления результат запроса.. чтобы один не блокировал остальные
Просто я почитал вот тут как работает реактор
http://stackoverflow.com/questions/80617/asychronous-programming-in-python-twisted/81456И судя по всему он достает последовательно вызовы которые зарегистрированы (коллбэки) и запускает, те судя по этому коду если вызов (коллбэк) который он запускает тяжелый, то следующие вызовы не смогут быть запущены пока он не посчитается .. Хотя могут прийти уже запросы и ждать обработки. Те чтобы получить паралельность обработки я внутри коллбэка сам должен вынести расчеты в отдельный поток например, так?
Андрей Светлов
Май 13, 2010 11:01:06
Уважаемый Evg.
Видимо, вы не вполне понимете работу реактивных систем.
Давайте сначала.
Есть фабрика->протокол->транспорт.
Протокол - основа общения. Один протокол на соединение.
Фабрика создает протоколы. TCP сервер создает протокол на каждое соединение, которых может быть много.
Транспорт - абстракция дескриптора, работающего в неблокирующем режиме. Сокет (кстати, у них много разновидностей - и много классов реализации) - только один из видов дескриптора. Существуют еще и такие как последовательный порт или inotify - система извещений об изменениях файловой системы.
Реактор на первых порах вообще как-бы в стороне. Он обеспечивает работу всей машинерии и светит наружу фабричные методы: сделать сокет такой-то, вызвать код через надцать секунд.
Обратите внимание: deferred в этой схеме пока что вообще отсутствуют.
Для сложных систем (считайте - практически для всего) создают протоколы второго уровня. Например, в случае с HTTP сервером вы будете работать с Resource, имея http request с уже подготовленными заголовками - и ответ тоже не опускается до байтов - устанавливаются нужные заголовки и заполняется тело. Возможно, не за один раз.
Теперь о “тяжелых запросах”. Тяжесть - она бывает разная. Одно дело, когда нужно много считать - и тогда выручает deferToThread. Вообще-то это случается редко. Гораздо чаще имеем другую ситуацию: чтобы ответить, нам нужно сначала спросить кого-то еще. Базу данных, другой сетевой ресурс и т.д.
Так вот, мы спрашиваем и в ответ получаем deferred. Сами в таком случае говорим “еще не готово” (чаще всего тоже возвращая deferred, в twisted.web нужно ответить NOT_DONE_YET). Реактор переключается на следующую задачу. Когда данные прийдут - сработает callback, мы их быстро обработаем в главном потоке (процессоры нынче ух как быстрые) - и наконец-то вернем ответ ожидающему концу соединения. Или запустим новый запрос и станем ждать его - опять освободив реактор.
Хорошо, если наши внутренние запросы поддерживают неблокирующие операции. Технически для этого нужно иметь нечто, что можно засунуть в select, poll и т.д. Иногда такое невозможно, потому что имеющийся интерфейс может работать только в блокирующем режиме (подавляющее большинство баз данных, например). В таком случае опять же делаем deferToThread и ждем. В отдельный поток лучше выносить только этот blocking call. Сейчас объясню почему.
На моей текущей работе нужно взаимодействовать с libvirt - эта такая либа для управления виртуальными машинами. Интерфейс - сугубо блокирующий.
Первая реализация мешала все в кучу: когда deferToThread а когда и просто вызов. В результате было не очевидно, исполняется ли метод в потоке реактора или мы уже в thread pool. Поскольку все отследить “в голове” было очень тяжело, иногда цепочка исполнения уводила на блокирующий вызов из главного потока. И все затыкалось.
Следующий естественный шаг - декомпозиция. Написали пару-тройку классов-оберток. Вызов каждого метода делал deferToThread, оборачивая блокирующий код и возвращая deferred. Обертки были тривиальные, а весь остальной код стал уверенно вращаться в реакторе. Проблема исчезла.
Я достаточно хорошо описал “параллельность” в том смысле, в котором ее понимают реактивные системы?
Кстати, если заменить twisted на GUI с событиями - суть почти не меняется.
Evg
Май 13, 2010 13:53:22
Да, спасибо за развернутый ответ)
У меня вопрос насчет этого:
Андрей Светлов
Хорошо, если наши внутренние запросы поддерживают неблокирующие операции. Технически для этого нужно иметь нечто, что можно засунуть в select, poll и т.д. Иногда такое невозможно, потому что имеющийся интерфейс может работать только в блокирующем режиме (подавляющее большинство баз данных, например). В таком случае опять же делаем deferToThread и ждем. В отдельный поток лучше выносить только этот blocking call. Сейчас объясню почему.
Вот взять к примеру mysql, интерфейс же идет к нему через те же сокеты, те можно через не блокирующие сокеты с ним работать - т.е в этом плане не блокируется выполнение. Вот пришло например 5 запросов по чтению на сам mysql,я чесно говоря не в курсе таких деталей, но он теоретически может их выполнять в 5 потоках.. те получается что всеже интерфейс не блокирующий? Или же там все жестко блокируется и запросы строго по порядку пришедшие выполняются, в этом плане имеется ввиду?