Я пытаюсь иметь «родительскую цель», ожидающую завершения «параллельных дочерних целей», прежде чем что-то делать в «теле» «родительской цели».
В следующем примере сообщение @echo "Print this only after *ALL* the previous PARALLEL tasks are done!"
должно быть напечатано только после, все целевые foo-task1
, foo-task2
и foo-task3
завершены.
Если я удалю амперсанд (&
) в конце этих «дочерних целей» затем они становятся серийными, но это не ожидаемое поведение. Они должны вести себя аналогично «пулу потоков» или «пулу процессов». Я не знаю заранее, какая из этих целей завершится последней, поэтому я не могу удалить амперсанд (&
) из цели узкого места.
Эти цели представляют собой HTTP-вызовы удаленного REST API, для выполнения которых может потребоваться разное количество времени. Я хотел бы дождаться этих «параллельных вызовов REST», чтобы увидеть, удались ли они, прежде чем печатать последнее сообщение в foo-parallel
.
Эти «параллельные цели» возвращают stdout / stderr, поэтому я хочу, чтобы родительская цель могла дождаться их и выйти из строя в случае каких-либо ошибок.
Как я мог это сделать?
foo-task1:
@bash -c 'sleep 1 && echo "task1" &'
foo-task2:
@bash -c 'sleep 1 && echo "task2" &'
foo-task3:
@bash -c 'sleep 2 && echo "task3" &'
foo-parallel: foo-task3 foo-task2 foo-task1
@echo "Print this only after *ALL* the previous PARALLEL tasks are done!"
Версия команды make
:
$ make --version
GNU Make 4.1
Built for x86_64-pc-linux-gnu
Copyright (C) 1988-2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
редактировать:
Это немного другое определение цели (я менял sleep
раз, чтобы убедиться, что сообщения печатаются в отложенном порядке, а не в порядке в коде):
foo-parallel:
@bash -c 'sleep 5 && echo "task1" & \
sleep 1 && echo "task2" & \
sleep 2 && echo "task3" & \
wait; \
echo "done here!"'
печатает:
$ make foo-parallel
task2
task3
task1
done here!
но мне нужно поместить логику всех этих task1
, task2
и task3
в одну цель. Я хотел бы разделить эти задачи на разные цели.
Не знал об этом варианте J. Спасибо
Пожалуйста! Я тоже написал подробный ответ.
Думаю, вы хотите использовать опцию -j
:
foo-task1:
@sleep 1
@echo "task1"
foo-task2:
@sleep 1
@echo "task2"
foo-task3:
@sleep 2
@echo "task3"
foo-parallel: foo-task3 foo-task2 foo-task1
@echo "Print this only after *ALL* the previous PARALLEL tasks are done!"
И запустите make -j3 foo-parallel
- make
будет работать параллельно с правильной зависимостью. См. Больше раздел Параллельное выполнение:
GNU make knows how to execute several recipes at once. Normally, make will execute only one recipe at a time, waiting for it to finish before executing the next. However, the ‘-j’ or ‘--jobs’ option tells make to execute many recipes simultaneously.
Спасибо! Что, если есть цель all
, которая определена как all: foo-first-serial foo-3-parallel-targets foo-last-serial
, если я вызываю ее как make -j3 all
, будет ли она учитывать последовательную цепочку foo-first-serial
, затем 3 параллельные цели, затем foo-last-serial
?
@TPPZ: Я вижу, что MadScientist ответил вам под своим ответом.
Вы не можете этого сделать, не используя опцию make -j
. По умолчанию make строит одну цель, ожидает ее завершения, а затем строит следующую. Только с -j
он поддерживает одновременную работу нескольких целей.
foo-parallel: foo-task3 foo-task2 foo-task1
@echo "Print this only after *ALL* the previous PARALLEL tasks are done!"
foo-task1:
@sleep 1 && echo "task1"
foo-task2:
@sleep 1 && echo "task2"
foo-task3:
@sleep 2 && echo "task3"
Затем запустите: make -j
. Или, если вы хотите ограничить количество параллельных сборок, используйте make -j2
или сколько угодно.
Если вы ДЕЙСТВИТЕЛЬНО не хотите вводить -j
, вы можете использовать рекурсивную команду make:
foo-parallel:
@$(MAKE) -j foo-task1 foo-task2 foo-task3
@echo "Print this only after *ALL* the previous PARALLEL tasks are done!"
foo-task1:
@sleep 1 && echo "task1"
foo-task2:
@sleep 1 && echo "task2"
foo-task3:
@sleep 2 && echo "task3"
Однако это может вызвать странные предупреждения, если кто-то вызовет make -j
. В общем, я думаю, что лучше оставить это человеку, вызывающему make
, относительно того, сколько параллелизма они хотят для своей конкретной системы.
Спасибо! Что, если есть цель all
, которая определена как all: foo-first-serial foo-3-parallel-targets foo-last-serial
, если я вызываю ее как make -j3 all
, будет ли она учитывать последовательную цепочку foo-first-serial
, затем 3 параллельные цели, затем foo-last-serial
?
Хорошо, я попробовал второй вариант, который вы предложили из командной строки, например make all
, и он работает, как ожидалось (то есть foo-first-serial
, затем 3 параллельных цели, затем foo-last-serial
), но я вижу странное предупреждение, когда вызываю его как make -j2 aaa
, поэтому я угадайте, что это так, нет более чистого способа обеспечить какой-то поток, например последовательный> параллельный> последовательный?
Вам нужно будет указать необходимые предварительные условия: поэтому создайте foo-first-serial: | foo-3-parallel
, чтобы обеспечить этот порядок, и foo-3-parallel: | foo-last-serial
, чтобы обеспечить этот порядок. Здесь я использую предварительные условия только для заказа, но вы могли бы просто использовать вместо них обычные, если это работает лучше. Все make-файлы всегда должны явно объявлять свой граф зависимостей; не делать этого приводит к душевной боли и боли.
Плохая идея полагаться на порядок, в котором цели появляются в списках предварительных требований, чтобы обеспечить соблюдение порядка сборки. Если вам нужно заставить вещи строиться в определенном порядке, вы должны использовать операторы target / prerequisite, чтобы сообщить make об этом.
Хм, чтобы обеспечить выполнение: foo-first-serial
, затем трех параллельных целей foo-3-parallel
, затем последней последовательной цели foo-last-serial
, мне пришлось определить предварительные условия только для заказа (штука |
) по-другому, это: foo-3-parallel: | foo-first-serial
и foo-last-serial: | foo-3-parallel
. Затем из командной строки мне пришлось назвать это так: time make foo-last
(time
должен измерить необходимое время, чтобы проверить, параллельна ли параллельная вещь). Вот довольно непонятное объяснение труб: gnu.org/software/make/manual/make.html#Prerequisite-Types
Упс получил это задом наперед. Но да, в make вы ВСЕГДА указываете конечную цель, которую хотите построить, в командной строке, а не первую.
Почему бы вам не использовать опцию
-j
? Я считаю, что это лучший метод распараллеливания вmake
(см. gnu.org/software/make/manual/make.html#Parallel).