Прежде всего, общая проблема, которую я решаю, немного сложнее, чем я показываю здесь, поэтому, пожалуйста, не говорите мне «использовать потоки с блокировкой», поскольку это не решит мою реальную ситуацию без честного, ЧЕСТНОГО переписывания и рефакторинг.
У меня есть несколько приложений, которые я не могу модифицировать, которые берут данные из стандартного ввода и выводят их на стандартный вывод после выполнения своей магии. Моя задача состоит в том, чтобы связать несколько таких программ. Проблема в том, что иногда они задыхаются, и поэтому мне нужно отслеживать их прогресс, который выводится в STDERR.
pA = subprocess.Popen(CommandA, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# ... some more processes make up the chain, but that is irrelevant to the problem
pB = subprocess.Popen(CommandB, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=pA.stdout )
Теперь прямое чтение через pA.stdout.readline() и pB.stdout.readline() или обычные функции read() блокирует. Поскольку разные приложения выводят данные с разной скоростью и в разных форматах, блокировка невозможна. (И, как я писал выше, многопоточность не вариант, если только в крайнем, крайнем случае.) pA.communicate()
безопасен для взаимоблокировок, но, поскольку мне нужна информация в реальном времени, это тоже не вариант.
Таким образом, Google привел меня к этому асинхронному фрагмент подпроцесса в ActiveState.
Сначала все хорошо, пока не реализую. Сравнивая вывод cmd.exe pA.exe | pB.exe
, игнорируя тот факт, что оба вывода выводятся в одно и то же окно, создающее беспорядок, я вижу очень мгновенные обновления. Однако я реализую то же самое, используя приведенный выше фрагмент и объявленную там функцию read_some()
, и уведомление об обновлениях одного канала занимает более 10 секунд. Но когда это происходит, у него есть обновления, ведущие, например, к 40% прогресса.
Таким образом, я провел еще несколько исследований и увидел множество тем, касающихся PeekNamedPipe, анонимных дескрипторов и возврата 0 доступных байтов, даже если в канале есть информация. Поскольку эта тема оказалась за пределами моего опыта по исправлению или написанию кода, я пришел в Stack Overflow, чтобы найти руководство. :)
Моя платформа — 64-битная W7 с Python 2.6, приложения — 32-битные, если это имеет значение, и совместимость с Unix не вызывает беспокойства. Я даже могу иметь дело с полным решением ctypes или pywin32, которое полностью разрушает подпроцесс, если это единственное решение, если я могу асинхронно читать из каждого канала stderr с немедленной производительностью и без взаимоблокировок. :)
communicate
. В Windows он использует потоки, в то время как на других платформах используется выбор или опрос. Я не знаю, почему автор не использовал select в окнах, я не знаю окон. Попробуйте изучить и реализовать его с помощью select. - person mg.   schedule 31.03.2010select
работает только с сокетами, поэтому дляcommunicate
он совершенно непригоден. - person Alex Martelli   schedule 31.03.2010