Chạy các lệnh song song với giới hạn số lượng lệnh đồng thời


23

Tuần tự: for i in {1..1000}; do do_something $i; done- quá chậm

Song song: for i in {1..1000}; do do_something $i& done- tải quá nhiều

Làm thế nào để chạy các lệnh song song, nhưng không nhiều hơn, ví dụ, 20 trường hợp mỗi khoảnh khắc?

Bây giờ thường sử dụng hack like for i in {1..1000}; do do_something $i& sleep 5; done, nhưng đây không phải là một giải pháp tốt.

Cập nhật 2 : Chuyển đổi câu trả lời được chấp nhận thành tập lệnh: http://vi-server.org/vi/abul

#!/bin/bash

NUM=$1; shift

if [ -z "$NUM" ]; then
    echo "Usage: parallel <number_of_tasks> command"
    echo "    Sets environment variable i from 1 to number_of_tasks"
    echo "    Defaults to 20 processes at a time, use like \"MAKEOPTS='-j5' parallel ...\" to override."
    echo "Example: parallel 100 'echo \$i; sleep \`echo \$RANDOM/6553 | bc -l\`'"
    exit 1
fi

export CMD="$@";

true ${MAKEOPTS:="-j20"}

cat << EOF | make -f - -s $MAKEOPTS
PHONY=jobs
jobs=\$(shell echo {1..$NUM})

all: \${jobs}

\${jobs}:
        i=\$@ sh -c "\$\$CMD"
EOF

Lưu ý rằng bạn phải thay thế 8 khoảng trắng bằng 2 tab trước "i =" để làm cho nó hoạt động.

Câu trả lời:


15

GNU Parallel được tạo ra cho việc này.

seq 1 1000 | parallel -j20 do_something

Nó thậm chí có thể chạy các công việc trên máy tính từ xa. Dưới đây là ví dụ để mã hóa lại MP3 sang OGG bằng server2 và máy tính cục bộ chạy 1 công việc trên mỗi lõi CPU:

parallel --trc {.}.ogg -j+0 -S server2,: \
     'mpg321 -w - {} | oggenc -q0 - -o {.}.ogg' ::: *.mp3

Xem video giới thiệu về GNU Parallel tại đây:

http://www.youtube.com/watch?v=OpaiGYxkSuQ


Không biết về "moreutils" và rằng đã có một công cụ cho công việc. Nhìn và so sánh.
Vi.

1
Trong parallelmoreutils không phải là GNU Parallel và khá hạn chế trong các tùy chọn của nó. Lệnh trên sẽ không chạy với song song từ moreutils.
Ole Tange

1
Thêm một lựa chọn : xargs --max-procs=20.
Vi.

4

Không phải là một giải pháp bash, nhưng bạn nên sử dụng Makefile, có thể với -lkhông vượt quá một số tải tối đa.

NJOBS=1000

.PHONY = jobs
jobs = $(shell echo {1..$(NJOBS)})

all: $(jobs)

$(jobs):
    do_something $@

Sau đó, để bắt đầu 20 công việc một lúc

$ make -j20

hoặc để bắt đầu càng nhiều công việc càng tốt mà không vượt quá tải 5

$ make -j -l5

Có vẻ như giải pháp không hack cho bây giờ.
Vi.

2
echo -e 'PHONY=jobs\njobs=$(shell echo {1..100000})\n\nall: ${jobs}\n\n${jobs}:\n\t\techo $@; sleep `echo $$RANDOM/6553 | bc -l`' | make -f - -j20Bây giờ nó có vẻ hacky một lần nữa.
Vi.

@vi: oh my ....
Benjamin Bannier

Chuyển đổi giải pháp của bạn thành một kịch bản. Bây giờ nó có thể được sử dụng một cách dễ dàng.
Vi.

2

đăng tập lệnh trong câu hỏi với định dạng:

#!/bin/bash

NUM=$1; shift

if [ -z "$NUM" ]; then
    echo "Usage: parallel <number_of_tasks> command"
    echo "    Sets environment variable i from 1 to number_of_tasks"
    echo "    Defaults to 20 processes at a time, use like \"MAKEOPTS='-j5' parallel ...\" to override."
    echo "Example: parallel 100 'echo \$i; sleep \`echo \$RANDOM/6553 | bc -l\`'"
    exit 1
fi

export CMD="$@";

true ${MAKEOPTS:="-j20"}

cat << EOF | make -f - -s $MAKEOPTS
PHONY=jobs
jobs=\$(shell echo {1..$NUM})

all: \${jobs}

\${jobs}:
        i=\$@ sh -c "\$\$CMD"
EOF

Lưu ý rằng bạn phải thay thế 8 khoảng trắng bằng 2 tab trước "i =".


1

Một ý tưởng đơn giản:

Kiểm tra i modulo 20 và thực hiện lệnh chờ shell trước khi do_s Something.


Nó sẽ đợi tất cả các tác vụ hiện tại hoàn thành (tạo độ trễ trong số lượng âm mưu của nhiệm vụ) hoặc chờ một tác vụ cụ thể có thể bị đình trệ trong thời gian dài hơn (một lần nữa tạo độ trễ trong trường hợp này)
Vi.

@Vi: Shell chờ đợi là dành cho tất cả các tác vụ nền thuộc về shell này.
harrymc

1

Bạn có thể sử dụng psđể đếm xem có bao nhiêu quy trình bạn đang chạy và bất cứ khi nào điều này giảm xuống dưới một ngưỡng nhất định, bạn sẽ bắt đầu một quy trình khác.

Mã giả:

i = 1
MAX_PROCESSES=20
NUM_TASKS=1000
do
  get num_processes using ps
  if num_processes < MAX_PROCESSES
    start process $i
    $i = $i + 1
  endif
  sleep 1 # add this to prevent thrashing with ps
until $i > NUM_TASKS

1
for i in {1..1000}; do 
     (echo $i ; sleep `expr $RANDOM % 5` ) &
     while [ `jobs | wc -l` -ge 20 ] ; do 
         sleep 1 
     done
done

Có thể là while [ `jobs | wc -l` -ge 20]; dogì?
Vi.

chắc chắn, nhưng trong mẫu của tôi, sau đó tôi phải tính toán njobshai lần và hiệu suất khá quan trọng trong các tập lệnh shell chạy các tác vụ ngủ;)
msw

Ý tôi là phiên bản của bạn không hoạt động như mong đợi. Tôi thay đổi sleep 1đến sleep 0.1và nó bắt đầu njobs trung bình 40-50 thay vì 20. Nếu có hơn 20 công việc chúng ta cần phải chờ đợi cho bất kỳ công việc được hoàn thành, không chỉ chờ đợi 1 giây.
Vi.

0

bạn có thể làm nó như thế này

threads=20
tempfifo=$PMS_HOME/$$.fifo

trap "exec 1000>&-;exec 1000<&-;exit 0" 2
mkfifo $tempfifo
exec 1000<>$tempfifo
rm -rf $tempfifo

for ((i=1; i<=$threads; i++))
do
    echo >&1000
done

for ((j=1; j<=1000; j++))
do
    read -u1000
    {
        echo $j
        echo >&1000
    } &
done

wait
echo "done!!!!!!!!!!"

sử dụng các ống có tên, mỗi lần, nó chạy song song 20 vỏ phụ.

Hy vọng nó sẽ giúp :)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.