Блоки подпроцесса Python во встроенной среде - PullRequest
0 голосов
/ 30 мая 2019

Мой скрипт принимает большое количество файлов на вход, вызывая внешние программы, используя subprocess.call().Он отлично работает с обычных консолей ОС, но зависает после обработки примерно 10 файлов при работе во встроенной среде с использованием встроенного Python 2.7.x.

Я ссылался на различные похожие вопросы и не смог найти один работающийдля меня:

Python: subprocess.Popen и subprocess.call зависают

Подпроцесс Python зависает

Pythonвызов подпроцесса зависает

и это углубленное обсуждение: https://thraxil.org/users/anders/posts/2008/03/13/Subprocess-Hanging-PIPE-is-your-enemy/

Все они подразумевают уязвимую буферизованную PIPE и предлагают использовать файловые объекты для stdout и stderr.Поэтому я также добавил временный текстовый файл, открыл его и передал в stdout и stderr из subprocess.call().Это тоже не сработало.

Мой старый код довольно прост:

# script1.py

folder = "/path/to/my/folder"
for root, dirs, files in os.walk(folder):
    for file in files:
        path = join(root, file)
        try:
            cmd = ['dir', path, '1>&2']
            _logger.debug(' '.join(cmd))
            completed = subprocess.call(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        except subprocess.CalledProcessError as err:
            _logger.debug(err)
        else:
            _logger.debug('returncode: {}'.format(completed))
print('all done!!')

основной скрипт:

try:
    cmd = ['python', 'script1.py', '1>&2']
    ue.log(' '.join(cmd))
    completed = subprocess.call(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as err:
    _logger.error(err)

Я смешал и ...Подобрал пару решений stdout / stderr.включая добавление:

with open(join(_script_dir, 'tmp.txt'), 'w') as tmp:
    #
    # old code
    #
     completed = subprocess.call(cmd, shell=True, stdout=tmp, stderr=tmp)

Ни один не работал.

Ответы [ 2 ]

0 голосов
/ 15 июня 2019

Существует решение проблемы подвесной трубы, которое должно использовать from multiprocessing import Manager Вы устанавливаете очередь задач:

mgr = Manager()

task_queue = mgr.Queue()

Передать его процессу в качестве аргумента:

gmat_args.append([gmat_arg, task_queue])
...
pool = Pool(processes=nrunp, maxtasksperchild=20)
...
results = pool.map(run_gmat, gmat_args, chunksize=ninstances)

Процесс записывает в пул:

def run_gmat(args):
    q = args[1]

    scriptname = os.path.basename(args[0])

    proc = sp.Popen(['gmat', '-m', '-ns', '-x', '-r', str(args[0])])

    (outs, errors) = proc.communicate(timeout=cpto)

    outs = outs.decode('UTF-8')

    q.put(filter_outs(outs, scriptname))

Затем, вернувшись в основной процесс, вы читаете очередь и регистрируете ее:

while 1:

    qout = task_queue.get(cpto)   

    logging.info(qout)

    if task_queue.qsize() < 1:

        break

Не могу сказать, что это идеально, я выполняю более 4000 заданий и получаю 2-3 тайм-аута, которые, похоже, связаны с файловым вводом / выводом (задание записывает массивный файл отчета по завершении). Чтобы предотвратить зависание, я ловлю исключение тайм-аута, записываю последний stdout, stderr в очередь и убиваю задание. Я теряю файлы отчетов, но могу видеть тайм-аут в журнале из очереди, так что это просто вопрос повторного запуска этих 2-3 заданий.

0 голосов
/ 30 мая 2019

Я закончил тем, что выбросил все каналы и stdout / stderr, и использовал значения по умолчанию для subprocess.Popen() аргументов Python 3.7.2, то есть None для stdout / stderr, и удалил 1>&2. Таким образом, в основном просто не надо извлекать выходные данные другой программы в реальном времени, когда программа работает с большим объемом ввода и потенциально генерирует огромную волну вывода через узкий и скрытый канал. Я перешел к использованию только файлов журналов, написанных для каждого отдельного сценария.

Я гораздо счастливее с этим решением, чем вслепую пытаться использовать всевозможные трюки с UNIX для решения проблемы с каналом в Windows.

P.S., В некоторых ответах из моих ссылок, приведенных выше, предлагалось shell=False, я обнаружил, что раздражает то, что встроенная среда порождает множество плавающих окон консоли из каждого subprocess.call().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...