Bash 스크립트에서 한 번에 100개의 프로세스 시작

Bash 스크립트에서 한 번에 100개의 프로세스 시작

Bash 스크립트에는 다음과 같은 프로그램이 있습니다

for i in {1..1000}
do
   foo i
done

매개변수를 사용하여 함수를 foo1000번 호출합니다.i

여러 프로세스에서 실행되지만 동시에 실행되지는 않으려면 어떻게 해야 합니까?

그래서 내가 가지고 있다면

for i in {1..1000}
do
   foo i &
done

1000개의 프로세스가 모두 동시에 시작되는데 이는 내가 원하는 것이 아닙니다.

항상 100개의 프로세스가 실행되도록 하는 방법이 있습니까? 일부 프로세스가 완료되면 1000번의 반복이 모두 완료될 때까지 일부 새로운 프로세스가 시작됩니다. 또는 100개가 모두 완료될 때까지 기다린 다음 100개를 더 실행할 수도 있습니다.

답변1

zsh대신 사용하십시오 bash:

autoload -Uz zargs
zargs -P100 -I{} -- {1..1000} -- foo {}

하지만 GNU가 있다면 xargs다음과 같이 할 수도 있습니다( zsh, ksh93또는 에서 bash):

xargs -I{} -P100 -a <(echo {1..1000}) foo {}

foo그러나 그것은 독립적인 명령이어야 한다. 쉘 함수나 내장 함수와 함께 사용할 수 없습니다.

zsh' 는 zargs차례로 실행됩니다. 100개 작업을 시작하고 모든 작업이 반환될 때까지 기다린 후 100개 작업의 다음 배치를 시작합니다. 반면 GNU는 xargs최대 100개의 작업을 계속 실행하려고 합니다. 100개의 작업을 시작한 다음 다른 작업을 시작하여 한 번에 하나씩 완료합니다.

이 동작을 얻으려면 zsh에서 백그라운드 프로세스가 반환될 때마다 트리거되는 xargsSIGCHLD에서 작업 풀을 시작하고 관리할 수 있습니다 .trap

(
  todo=( {1..1000} ) max=100

  TRAPCHLD() {
    while (( $#jobstates < max && $#todo )); do
      foo $todo[1] & shift 1 todo
    done
  }

  : start &
  while (( $#todo )) wait
)

여기서는 새 작업 목록을 가져오기 위해 서브셸에서 이를 실행해야 합니다. TRAPCHLD 트랩이 실행되는 동안 SIGCHLD가 차단되므로 트랩이 자체적으로 다시 입력되어서는 안 됩니다. 이는 경쟁 조건이나 목록에 대한 동시 액세스를 방지해야 하는 필요성을 방지해야 합니다 $todo.

답변2

그룹으로 실행할 수 있으면 루프를 중첩하십시오.

#! /bin/bash

date '+%T.%N'
for j in {1..3}; do
    for k in {1..3}; do
        (( ++i ))
        ( sleep 2.0 && printf 'Foo %d\n' $i ) &
    done
    wait
    date '+%T.%N'
    printf 'Batch %d ends\n' $j 
done
date '+%T.%N'

결과는 시간 중복을 보여줍니다.

$ ./aBatch
19:55:17.078476713
Foo 1
Foo 2
Foo 3
19:55:19.094302514
Batch 1 ends
Foo 4
Foo 6
Foo 5
19:55:21.114530543
Batch 2 ends
Foo 7
Foo 9
Foo 8
19:55:23.132184671
Batch 3 ends
19:55:23.135792952
$ 

이는 GNU 병렬성에서도 마찬가지입니다. 이 방법의 장점은 실행이 서로 다른 시간에 실행되는 경우 parallel일괄 처리의 다른 프로세스를 기다릴 필요 없이 추가 프로세스가 시작된다는 것입니다.

#! /bin/bash
#.. The script ./aFoo

    sleep 2 && printf 'Foo %d\n' $1

주문하다:

$ date '+%T.%N'; parallel -j 3 ./aFoo -- {1..9}; date '+%T.%N'
20:11:44.446042653
Foo 3
Foo 1
Foo 2
Foo 4
Foo 5
Foo 6
Foo 7
Foo 8
Foo 9
20:11:50.503324162
$ 

답변3

다음은 100개 단위로 분할하는 간단한 방법입니다. (bash)

for i in {1..1000}
do
   foo "$i" &
   (( i % 100 )) || wait
done
wait

동일한 셸에서 실행되는 다른 백그라운드 작업이 없다고 가정하고 100개의 작업을 시작한 다음 모든 작업이 완료될 때까지 기다린 다음 또 다른 100개의 작업을 시작하고 마지막으로 나머지 작업이 완료될 때까지 기다립니다. (1000과 100은 나머지가 없지만 다른 경우에는 있을 수 있습니다)

++n루프 변수가 숫자가 아닌 경우 대신 표현식에서 사용할 수 있습니다i

n=0
for i in *
do
   foo "$i" &
   (( ++n % 100 )) || wait
done
wait

답변4

GNU Parallel은 바로 이런 상황을 위해 만들어졌습니다:

parallel foo ::: {1..1000}

foo n1000개의 작업이 모두 실행될 때까지 CPU 스레드당 하나씩(n = 1..1000) 실행 됩니다 . 한 작업이 완료되면 다른 작업이 시작됩니다.

parallel -j100 foo ::: {1..1000}

foo n1000개의 작업이 모두 실행될 때까지 100개(여기서 n = 1..1000)가 실행됩니다 .

GNU Parallel은 출력을 직렬화하므로 두 개의 foos가 동시에 인쇄되어도 출력이 왜곡되지 않습니다.

GNU Parallel에는 병렬화를 더 쉽게 만드는 다른 많은 기능이 있습니다. 1장과 2장을 읽는 데 20분 정도 소요됩니다.https://zenodo.org/record/1146014귀하의 명령줄이 감사할 것입니다.

관련 정보