Запустите два процесса одновременно и соберите результаты процесса, завершенного ранее - PullRequest
1 голос
/ 23 марта 2019

Предположим, я хочу запустить две команды c1 и c2, которые по сути обрабатывают (но не изменяют) один и тот же фрагмент данных в Linux.

Сейчас я хотел бы запустить их одновременно и посмотреть, какой из них завершается быстрее, после завершения одного процесса я соберу его вывод (может быть помещен в файл с c1 >> log1.txt) и завершудругой процесс.

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

======================= update

Я пробовал следующий набор сценариев, но он вызывает бесконечный цикл на моем компьютере:

import os


os.system("./launch.sh")

launch.sh

#!/usr/bin/env bash

rm /tmp/smack-checker2
mkfifo /tmp/smack-checker2

setsid bash -c "./sleep60.sh ; echo 1 > /tmp/run-checker2" &
pid0=$!
setsid bash -c "./sleep10.sh ; echo 2 > /tmp/run-checker2" &
pid1=$!

read line </tmp/smack-checker2
printf "Process %d finished earlier\n" "$line"
rm /tmp/smack-checker2
eval kill  -- -\$"pid$((line ^ 1))"

sleep60.sh

#!/usr/bin/env bash

sleep 60

sleep10.sh

#!/usr/bin/env bash

sleep 10

Ответы [ 3 ]

2 голосов
/ 23 марта 2019

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

c1 & P1=$!
c2 & P2=$!
wait -n  # wait for either one to exit
if ! kill $P1; then
  # failure to kill $P1 indicates c1 finished first
  kill $P2
  # collect c1 results...
else
  # c2 finished first
  kill $P1
  # collect c2 results...
fi

См. help wait или man bash для документации.

1 голос
/ 23 марта 2019

Я бы запустил 2 процесса и заставил их записать в общий именованный канал после их завершения.Чтение из именованного канала является операцией блокировки, поэтому вам не нужны смешные инструкции sleep внутри цикла.Это будет:

#!/usr/bin/env bash

mkfifo /tmp/run-checker

(./sleep60.sh ; echo 0 > /tmp/run-checker) &
(./sleep10.sh ; echo 1 > /tmp/run-checker) &

read line </tmp/run-checker
printf "Process %d finished earlier\n" "$line"
rm /tmp/run-checker
kill -- -$$

sleep60.sh:

#!/usr/bin/env bash

sleep 60

sleep10.sh:

#!/usr/bin/env bash

sleep 10

РЕДАКТИРОВАТЬ:

Если вы собираетесь вызывать скрипт из скрипта Python следующим образом:

#!/usr/bin/env python3

import os
os.system("./parallel.sh")
print("Done")

вы получите:

Process 1 finished earlier
./parallel.sh: line 11: kill: (-13807) - No such process
Done

Это потому, что kill -- -$$ пытается отправитьTERM сигнализирует группе процессов, как указано в man 1 kill:

-n

, где n больше 1. Все процессы в группе процессов n сигнализируются.Когда задан аргумент в форме '-n', и он предназначен для обозначения группы процессов, либо сначала должен быть указан сигнал, либо перед аргументом должна стоять опция '-', в противном случае он будет приняткак сигнал для отправки.

Работает, когда вы запускаете параллельный.sh из терминала, потому что $$ - это PID подоболочки, а также группы процессов.Я использовал его, потому что очень удобно убивать61.pro, process0 или process1 и всех их детей за один раз.Тем не менее, при вызове parallel.sh из скрипта Python $$ больше не обозначает группу процессов и kill -- завершается неудачей.

Вы можете изменить файл parallel.sh следующим образом:

#!/usr/bin/env bash

mkfifo /tmp/run-checker

setsid bash -c "./sleep60.sh ; echo 0 > /tmp/run-checker" &
pid0=$!
setsid bash -c "./sleep10.sh ; echo 1 > /tmp/run-checker" &
pid1=$!

read line </tmp/run-checker
printf "Process %d finished earlier\n" "$line"
rm /tmp/run-checker
eval kill  -- -\$"pid$((line ^ 1))"

Теперь он будет работать и при вызове из скрипта Python.Последняя строка

eval kill  -- -\$"pid$((line ^ 1))"

убивает pid0, если pid1 завершил ранее, или pid0, если pid1 завершил ранее, используя двоичный оператор ^ для преобразования 0 в 1 и наоборот.Если вам это не нравится, вы можете использовать более подробную форму:

if [ "$line" -eq "$pid0" ]
then
    echo kill  "$pid1"
    kill  -- -"$pid1"
else
    echo kill  "$pid0"
    kill -- -"$pid0"
fi
1 голос
/ 23 марта 2019

Может ли этот фрагмент дать вам некоторое представление?

#!/bin/sh

runproc1() {
  sleep 5
  touch proc1    # file created when terminated
  exit
}

runproc2() {
  sleep 10
  touch proc2    # file created when terminated
  exit
}

# remove flags
rm proc1
rm proc2

# run processes concurrently
runproc1 &
runproc2 &

# wait until one of them is finished
while [ ! -f proc1 -a ! -f proc2 ]; do
  sleep 1
  echo -n "."
done

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

Запуск этого точного сценария занимает около 5 секунд, а затем завершается. Я вижу, что файл "proc1" создан без proc2. Через несколько секунд (5, если быть точным) также создается «proc2». Это означает, что даже когда сценарий завершается, любая незавершенная работа продолжает выполняться.

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