2009-07-15 22 views
38

Để tối đa hóa việc sử dụng CPU (tôi chạy mọi thứ trên một Lenny Debian trong EC2) Tôi có một kịch bản đơn giản để khởi động công việc song song:Chờ cho việc làm nền bash trong kịch bản được hoàn thành

#!/bin/bash 

for i in apache-200901*.log; do echo "Processing $i ..."; do_something_important; done & 
for i in apache-200902*.log; do echo "Processing $i ..."; do_something_important; done & 
for i in apache-200903*.log; do echo "Processing $i ..."; do_something_important; done & 
for i in apache-200904*.log; do echo "Processing $i ..."; do_something_important; done & 
... 

Tôi khá hài lòng với giải pháp làm việc này, tuy nhiên tôi không thể tìm ra cách viết thêm mã mà chỉ thực thi khi tất cả các vòng lặp đã được hoàn thành.

Có cách nào để kiểm soát điều này không?

Trả lời

63

Có một lệnh nội trang bash cho điều đó.

wait [n ...] 
     Wait for each specified process and return its termination sta‐ 
     tus. Each n may be a process ID or a job specification; if a 
     job spec is given, all processes in that job’s pipeline are 
     waited for. If n is not given, all currently active child pro‐ 
     cesses are waited for, and the return status is zero. If n 
     specifies a non-existent process or job, the return status is 
     127. Otherwise, the return status is the exit status of the 
     last process or job waited for. 
+1

Điều đó đã nhanh chóng và giải quyết được vấn đề của tôi, cũng kiếm được, cảm ơn! – mark

+13

gợi ý sử dụng '' 'chờ $ (jobs -p)' '' để chờ cho các công việc mới được tạo ra. – lambacck

+0

cảm ơn bạn @lambacck – pabloa98

20

Sử dụng GNU Parallel sẽ làm cho kịch bản của bạn thậm chí ngắn hơn và có thể hiệu quả hơn:

parallel 'echo "Processing "{}" ..."; do_something_important {}' ::: apache-*.log 

này sẽ chạy một công việc cho mỗi lõi CPU và tiếp tục làm điều đó cho đến khi tất cả các file được xử lý.

Giải pháp của bạn về cơ bản sẽ chia công việc thành các nhóm trước khi chạy. Dưới đây 32 việc làm trong 4 nhóm:

Simple scheduling

GNU Parallel thay spawns một quá trình mới khi một kết thúc - giữ CPU hoạt động và do đó tiết kiệm thời gian:

GNU Parallel scheduling

Để tìm hiểu thêm:

0

Đây là giải pháp thô của tôi:

function run_task { 
     cmd=$1 
     output=$2 
     concurency=$3 
     if [ -f ${output}.done ]; then 
       # experiment already run 
       echo "Command already run: $cmd. Found output $output" 
       return 
     fi 
     count=`jobs -p | wc -l` 
     echo "New active task #$count: $cmd > $output" 
     $cmd > $output && touch $output.done & 
     stop=$(($count >= $concurency)) 
     while [ $stop -eq 1 ]; do 
       echo "Waiting for $count worker threads..." 
       sleep 1 
       count=`jobs -p | wc -l` 
       stop=$(($count > $concurency)) 
     done 
} 

Ý tưởng là để sử dụng "công việc" để xem có bao nhiêu trẻ em đang hoạt động ở chế độ nền và đợi cho đến khi con số này giảm (một đứa trẻ thoát). Khi một đứa trẻ tồn tại, nhiệm vụ tiếp theo có thể được bắt đầu.

Như bạn có thể thấy, cũng có một chút logic bổ sung để tránh chạy cùng một thử nghiệm/lệnh nhiều lần. Nó thực hiện công việc cho tôi .. Tuy nhiên, logic này có thể bị bỏ qua hoặc cải thiện hơn nữa (ví dụ: kiểm tra dấu thời gian tạo tệp, tham số đầu vào, v.v.).

1

tôi phải làm điều này thời gian gần đây và đã kết thúc với các giải pháp sau đây:

while true; do 
    wait -n || { 
    code="$?" 
    ([[ $code = "127" ]] && exit 0 || exit "$code") 
    break 
    } 
done; 

Dưới đây là cách hoạt động:

wait -n thoát càng sớm là một trong những (có khả năng nhiều) công việc nền thoát. Nó luôn luôn đánh giá là đúng và vòng lặp tiếp tục cho đến:

  1. Mã thoát 127: công việc nền cuối cùng đã thoát thành công.Trong trường hợp đó là , chúng tôi bỏ qua mã thoát và thoát khỏi hệ vỏ con có mã 0.
  2. Bất kỳ công việc nào trong nền không thành công. Chúng tôi chỉ thoát khỏi sub-shell với mã thoát đó.

Với set -e, điều này sẽ đảm bảo rằng tập lệnh sẽ chấm dứt sớm và chuyển qua mã thoát của bất kỳ tác vụ nền nào bị lỗi.

Các vấn đề liên quan