Скрученный - Отложенный, если он не асинхронный, то какой в ​​этом смысл? - PullRequest
1 голос
/ 07 мая 2019

Мне было поручено изучить Twisted.Я также немного новичок в Python в целом, но использовал другие современные языки программирования.

При чтении документации Twisted я продолжаю сталкиваться с примерами, которые

  • Не полные исполняемые примеры
  • Запуск в одном потоке

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

Я вижу, что она имеет некоторые встроенные асинхронные механизмы, но ни один из них не предоставляет пользователю средства для создания пользовательских асинхронных задач с привязкой к ЦП, как«Задачи» в C # или «работа» с boost :: asio в C ++, которые будут выполняться параллельно основному потоку.

Я вижу, что Twisted предоставляет средства для асинхронного ожидания ввода-вывода и выполнения действий втот же поток во время ожидания, если мы ожидаем:

  • сеть читает и пишет
  • ввод клавиатурыut

Здесь также показано, как:

  • Выполнить некоторую интеграцию с наборами инструментов GUI, чтобы использовать их цикл событий, но не вдаваться в подробности.
  • Планирование задач с использованием реактора по таймеру, но не выполняет эту задачу параллельно с чем-либо еще

В нем говорится об асинхронном / ожидании, но это только для Python 3и я использую python 2.7

Я подумал, что какой-то способ объединения потоков должен быть встроен в реактор, но потом, когда я читаю о реакторе, он говорит, что все работает в основном потоке в реакторе.().

Итак, я запутался.

  • Какой смысл откладывать, создавать цепочку обратного вызова и реагировать на результаты, если мы ничего не выполняем впараллельный?
  • Если мы выполняем асинхронный код, как мы создаем наши собственные пользовательские асинхронные функции?(см. ключевое слово async в C #)

В других языках я мог бы создать асинхронную задачу для подсчета от 1 до 10, в то время как в главном потоке я мог бы считать от 'a' до 'z'в то же время.Когда задача будет выполнена, я получу уведомление через обратный вызов потока из пула потоков.Если бы я захотел, у меня была возможность синхронизироваться, вызвав какой-нибудь метод wait.В то время как определение «асинхронный» включает в себя только публикацию задачи, получение результата и обратный вызов, когда оно выполнено… Я никогда не видел, чтобы оно использовалось без параллельной работы.

1 Ответ

2 голосов
/ 07 мая 2019

Я отвечу на ваши вопросы (и утверждения, которые кажутся запутанными) один за другим:

"Примеры, которые не являются полными"

Повторение того, что я опубликовал в комментариях: см.два моих предыдущих ответа на полные примеры (https://stackoverflow.com/a/30399317/3334178 & https://stackoverflow.com/a/23274411/3334178) и прохождение Twisted Введение Крондо

Вы сказали, что вы обесцениваете их, потому что "Примерысетевой код в витой, который имеет встроенную и скрытую асинхронность ".Я не согласен с этим утверждением и объясню это в следующем разделе.

«Примеры не являются асинхронными»

Когда вы говорите об «асинхронном программировании», напрасно скручивая питоны / торнадо / асинцио(или Node.JS или C select / poll / kpoll) вы говорите о модели / шаблоне программирования, который позволяет программисту формировать свой код так, чтобы его части могли работать, в то время как другие части были заблокированы (почти во всехслучаи, когда блокировка вызвана тем, что часть программы вынуждена ждать ввода-вывода).

Эти библиотеки / языки, несомненно, будут иметь способы выполнения потоков и / или многопроцессорной обработки, но это слои, привитые поверхАсинхронный дизайн - и если это действительно то, что вам нужно (т.е. у вас есть исключительно связанные с процессором потребности), асинхронные системы будут плохим выбором.

Давайте использовать ваш «скрытый» комментарий, чтобы войти в этонемного больше

"Примеры сети - асыч, но асинхронность встроена и скрыта"

TФундаментальный элемент асинхронного дизайна заключается в том, что вы пишете свой код, чтобы он никогда не блокировался для ввода-вывода - вы вызывали сеть , но на самом деле мы говорим о сети / диске / клавиатуре / мыши / звуке /serial - все, что (по какой-либо причине) может работать медленнее, чем процессор ( и что в ОС есть дескриптор файла для ).

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

Позвольте мне использовать клавиатуру в качестве примера.

В коде синхронизации вы должны использовать input или read - , и программа приостановит , ожидая ввода этой строки (или клавиши).

В асинхронном коде (по крайней мере, в таких характерных реализациях, как витая) вы извлекаете дескриптор файла для «ввода» и регистрируете его с помощью функции обратного вызова, которая будет вызываться при изменении дескриптора файла, в асинхронный уровень на уровне ОСengine (select, poll, kpoll и т. д.)

Акт регистрации - которая почти не занимает времени ПОЗВОЛЯЕТ ВАМ запускать другую логику, покаЛогика клавиатуры ожидает события клавиатуры (см. строку stdio.StandardIO(keyboardobj,sys.stdin.fileno()) в конце моего примера кода в https://stackoverflow.com/a/30399317/3334178).

"[Это] наводит меня на мысль, что есть и другие способы использования отложенныхс асинхронным "

deferreds не волшебство. Это просто умные списки функций обратного вызова. Есть множество умных способов их объединения в цепочку, но в конце концов, они просто инструмент, который поможет вам воспользоватьсяиз лogic выше

"Это также говорит об асинхронности / ожидании, то есть только для Python 3, и я использую Python 2.7"

async и await - это всего лишь способ Python 3делать то, что было сделано в python2 с @defer.inlineCallbacks и yield.Эти системы представляют собой ярлыки, которые переписывают код так, что для считывателя код выглядит и действует как код синхронизации, но при его запуске код преобразуется в «зарегистрировать обратный вызов и переместитьна "flow

", когда я читаю о реакторе, он говорит, что все работает в основном потоке в реакторе.run () "

Да, потому что (как указано выше) асинхронность не оожидание ввода-вывода - его не о многопоточности или мультиобработке

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

Также ваш комментарий, запрашивающий "пример, в котором мы считаем от 1 до 10 в некоторой отложенной функции, в то время как мы считаем от a до z в главном потоке?" не имеет смысла, когда речь идет об асинхронности (и потому, что вы говорите о «потоке» - это другая конструкция, и потому что это обе (вероятные) задачи ЦП), но я приведу другой пример, который учитывается при просмотре для ввода с клавиатуры (что определенно имеет смысл, когда речь идет об асинхронности:

#!/usr/bin/env python
#
# Frankenstein-esk amalgam of example code
#   Key of which comes from the Twisted "Chat" example
#   (such as: http://twistedmatrix.com/documents/12.0.0/core/examples/chatserver.py)

import sys # so I can get at stdin
import os # for isatty
import termios, tty # access to posix IO settings
from twisted.internet import reactor
from twisted.internet import stdio # the stdio equiv of listenXXX
from twisted.protocols import basic # for lineReceiver for keyboard
from twisted.internet import task


class counter(object):
    runs = 0

def runEverySecond():
    counter.runs += 1
    print "async counting demo: " + str(counter.runs)

# to set keyboard into cbreak mode - so keys don't require a CR before causing an event
class Cbreaktty(object):
    org_termio = None
    my_termio = None

    def __init__(self, ttyfd):
        if(os.isatty(ttyfd)):
            self.org_termio = (ttyfd, termios.tcgetattr(ttyfd))
            tty.setcbreak(ttyfd)
            print '  Set cbreak mode'
            self.my_termio = (ttyfd, termios.tcgetattr(ttyfd))
        else:
          raise IOError #Not something I can set cbreak on!

    def retToOrgState(self):
        (tty, org) = self.org_termio
        print '  Restoring terminal settings'
        termios.tcsetattr(tty, termios.TCSANOW, org)


class KeyEater(basic.LineReceiver):

    def __init__(self):
        self.setRawMode() # Switch from line mode to "however much I got" mode

    def rawDataReceived(self, data):
        key = str(data).lower()[0]
        if key == 'q':
            reactor.stop()
        else:
            print "--------------"
            print "Press:"
            print "      q  - to cleanly shutdown"
            print "---------------"

# Custom tailored example for SO:56013998
# 
# This code is a mishmash of styles and techniques. Both to provide different examples of how
# something can be done and because I'm lazy.  Its been built and tested on OSX and linux,
# it should be portable (other then perhaps termal cbreak mode).  If you want to ask
# questions about this code contact me directly via mail to mike at partialmesh.com
#
#
# Once running press any key in the window where the script was run and it will give
# instructions.  


def main():

    try:
      termstate = Cbreaktty(sys.stdin.fileno())
    except IOError:
      sys.stderr.write("Error: " + sys.argv[0] + " only for use on interactive ttys\n")
      sys.exit(1)

    keyboardobj = KeyEater()

    l = task.LoopingCall(runEverySecond)
    l.start(1.0) # call every second

    stdio.StandardIO(keyboardobj,sys.stdin.fileno())
    reactor.run()
    termstate.retToOrgState()
if __name__ == '__main__':
  main()

(Я знаю, что технически я не использовал отложенное - но у меня не хватило времени - и этот случай слишком прост, чтобы действительно нуждаться в этом (у меня нигде нет цепочки обратных вызовов, что и является отложенным) для))

...