2009-09-21 30 views
41

Tôi muốn tạo một phần mã của mình hiệu quả hơn. Tôi đang nghĩ đến việc biến nó thành nhiều quá trình và cho họ thực hiện 50/100 lần cùng một lúc, thay vì chỉ một lần.Ngã ba/Quy trình nhiều luồng | Bash

Ví dụ (giả):

for line in file; 
do 
foo; 
foo2; 
foo3; 
done 

Tôi muốn này cho vòng lặp để chạy nhiều lần. Tôi biết điều này có thể được thực hiện với forking. Nó sẽ trông như thế này?

while(x <= 50) 
parent(child pid) 
{ 
    fork child() 
} 
child 
{ 
    do 
    foo; foo2; foo3; 
    done 
    return child_pid() 
} 

Hoặc tôi đang nghĩ về điều này một cách sai lầm?

Cảm ơn!

Trả lời

2

Hãy để tôi thử ví dụ

for x in 1 2 3 ; do { echo a $x ; sleep 1 ; echo b $x ; } & done ; sleep 10 

Và sử dụng jobs để xem những gì đang chạy.

26

Tôi không biết bất kỳ lệnh gọi nào rõ ràng fork trong bash. Những gì bạn có thể muốn làm là thêm & vào lệnh mà bạn muốn chạy dưới nền. Bạn cũng có thể sử dụng & về chức năng mà bạn xác định trong một kịch bản bash:

do_something_with_line() 
{ 
    line=$1 
    foo 
    foo2 
    foo3 
} 

for line in file 
do 
    do_something_with_line $line & 
done 

EDIT: đặt một giới hạn về số lượng các quá trình nền đồng thời, bạn có thể thử một cái gì đó như thế này:

for line in file 
do 
    while [`jobs | wc -l` -ge 50 ] 
    do 
    sleep 5 
    done 
    do_something_with_line $line & 
done 
+1

Bạn đã miscapitalized các do_something ... tên ;-) –

+1

Got it - gì về khi tôi muốn chắc chắn rằng tôi chỉ là chạy 50 trường hợp cùng một lúc? Và - khi một trong những quy trình đó được thực hiện, hãy đảm bảo thêm 1 lần nữa được sinh ra. – Greg

+0

Sử dụng nội dung 'công việc' bash. –

46

Trong các tập lệnh bash (không tương tác) theo mặc định, JOB CONTROL bị tắt, do đó bạn không thể thực hiện các lệnh: job, fg và bg.

Đây là những gì làm việc tốt cho tôi:

#!/bin/sh 

set -m # Enable Job Control 

for i in `seq 30`; do # start 30 jobs in parallel 
    sleep 3 & 
done 

# Wait for all parallel jobs to finish 
while [ 1 ]; do fg 2> /dev/null; [ $? == 1 ] && break; done 

Dòng cuối cùng sử dụng "fg" để mang lại một công việc nền vào tiền cảnh. Nó thực hiện điều này trong một vòng lặp cho đến khi fg trả về 1 ($? == 1), mà nó làm khi không còn bất kỳ công việc nền nào nữa.

+16

Trong các tập lệnh bash, bạn có thể sử dụng 'wait', ví dụ:' sleep 3 & WAITPID = $ !; chờ $ WAITPID', hoặc đồng nhất các pids theo cách này 'WAITPIDS =" $ WAITPIDS "$!; ...; chờ $ WAITPIDS' –

+9

Hoặc chỉ "chờ". – lethalman

+0

Tôi sẽ làm 1000 thứ như thế nào, 50 lần một lần?trong một vòng lặp nói '$ (seq 1 1000)' – chovy

17

Với GNU Song song bạn có thể làm:

cat file | parallel 'foo {}; foo2 {}; foo3 {}' 

này sẽ chạy một công việc trên mỗi lõi CPU. Để chạy 50 làm:

cat file | parallel -j 50 'foo {}; foo2 {}; foo3 {}' 

Watch video giới thiệu để tìm hiểu thêm:

http://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

+2

Ngoại trừ màn hình nag, song song là khá thú vị. –

+1

Tôi sẽ thêm rằng song song đã được cài đặt trên hầu hết các hệ thống. Máy OS X 10.8.5 của tôi có nó. Đó là thời gian cho tôi để bụi các mạng nhện ra khỏi kịch bản shell của tôi và cập nhật của tôi cho các vòng để song song ... – labyrinth

+0

điều này dường như để có được lộn xộn thực sự khi sử dụng tìm kiếm/thay thế có ký tự phải được thoát. –

2

Dựa trên những gì tất cả các bạn chia sẻ tôi đã có thể đặt này với nhau:

#!/usr/bin/env bash 

VAR1="192.168.1.20 192.168.1.126 192.168.1.36" 

for a in $VAR1; do { ssh -t -t $a -l Administrator "sudo softwareupdate -l"; } & done; 
WAITPIDS="$WAITPIDS "$!;...; wait $WAITPIDS 
echo "Script has finished" 

Exit 1 

Điều này liệt kê tất cả các cập nhật trên mac trên ba máy cùng một lúc. Sau đó tôi sử dụng nó để thực hiện cập nhật phần mềm cho tất cả các máy khi tôi CAT ipaddress của tôi.txt

0

Dưới đây là chức năng điều khiển chủ đề của tôi:

#!/bin/bash 
# This function just checks jobs in background, don't do more things. 
# if jobs number is lower than MAX, then return to get more jobs; 
# if jobs number is greater or equal to MAX, then wait, until someone finished. 

# Usage: 
# thread_max 8 
# thread_max 0 # wait, until all jobs completed 

thread_max() { 
    local CHECK_INTERVAL="3s" 
    local CUR_THREADS= 
    local MAX= 
    [[ $1 ]] && MAX=$1 || return 127 

    # reset MAX value, 0 is easy to remember 
    [ $MAX -eq 0 ] && { 
     MAX=1 
     DEBUG "waiting for all tasks finish" 
    } 

    while true; do 
     CUR_THREADS=`jobs -p | wc -w` 

     # workaround about jobs bug. If don't execute it explicitily, 
     # CUR_THREADS will stick at 1, even no jobs running anymore. 
     jobs &>/dev/null 

     DEBUG "current thread amount: $CUR_THREADS" 
     if [ $CUR_THREADS -ge $MAX ]; then 
      sleep $CHECK_INTERVAL 
     else 
      return 0 
     fi 
    done 
} 
12

tôi không thích sử dụng wait bởi vì nó bị chặn cho đến khi thoát quá trình, mà không phải là lý tưởng khi có nhiều quá trình chờ đợi trên như tôi có thể' t nhận được cập nhật trạng thái cho đến khi quá trình hiện tại được thực hiện. Tôi thích sử dụng kết hợp của kill -0sleep cho điều này.

Cho một mảng pids để chờ, tôi sử dụng chức năng dưới đây waitPids() để nhận phản hồi liên tục về những gì vẫn đang chờ xử lý để kết thúc.

declare -a pids 
waitPids() { 
    while [ ${#pids[@]} -ne 0 ]; do 
     echo "Waiting for pids: ${pids[@]}" 
     local range=$(eval echo {0..$((${#pids[@]}-1))}) 
     local i 
     for i in $range; do 
      if ! kill -0 ${pids[$i]} 2> /dev/null; then 
       echo "Done -- ${pids[$i]}" 
       unset pids[$i] 
      fi 
     done 
     pids=("${pids[@]}") # Expunge nulls created by unset. 
     sleep 1 
    done 
    echo "Done!" 
} 

Khi tôi bắt đầu một quá trình ở chế độ nền, tôi thêm pid của nó ngay lập tức đến các mảng pids bằng cách sử dụng dưới đây chức năng tiện ích:

addPid() { 
    desc=$1 
    pid=$2 
    echo "$desc -- $pid" 
    pids=(${pids[@]} $pid) 
} 

Đây là một mẫu cho thấy làm thế nào để sử dụng:

for i in {2..5}; do 
    sleep $i & 
    addPid "Sleep for $i" $! 
done 
waitPids 

Và đây là cách phản hồi trông:

Sleep for 2 -- 36271 
Sleep for 3 -- 36272 
Sleep for 4 -- 36273 
Sleep for 5 -- 36274 
Waiting for pids: 36271 36272 36273 36274 
Waiting for pids: 36271 36272 36273 36274 
Waiting for pids: 36271 36272 36273 36274 
Done -- 36271 
Waiting for pids: 36272 36273 36274 
Done -- 36272 
Waiting for pids: 36273 36274 
Done -- 36273 
Waiting for pids: 36274 
Done -- 36274 
Done! 
0

Cách tiếp cận của haridsv rất tuyệt, nó cho phép linh hoạt để chạy thiết lập khe xử lý trong đó một số quy trình có thể được tiếp tục chạy với các công việc mới khi hoàn thành công việc, giữ toàn bộ tải. Dưới đây là mod của tôi để mã haridsv cho một bộ xử lý n-slot cho một 'lưới' của ngrid 'jobs' (tôi sử dụng nó cho lưới mô hình mô phỏng) Tiếp theo là kiểm tra đầu ra cho 8 công việc 3 tại một thời điểm, với tổng số chạy , nộp, hoàn thành và còn lại sản lượng

#!/bin/bash 
######################################################################## 
# see haridsv on forking-multi-threaded-processes-bash 
# loop over grid, submitting jobs in the background. 
# As jobs complete new ones are set going to keep the number running 
# up to n as much as possible, until it tapers off at the end. 
# 
# 8 jobs 
ngrid=8 
# 3 at a time 
n=3 
# running counts 
running=0 
completed=0 
# previous values 
prunning=0 
pcompleted=0 
# 
######################################################################## 
# process monitoring functions 
# 
declare -a pids 
# 
function checkPids() { 
echo ${#pids[@]} 
if [ ${#pids[@]} -ne 0 ] 
then 
    echo "Checking for pids: ${pids[@]}" 
    local range=$(eval echo {0..$((${#pids[@]}-1))}) 
    local i 
    for i in $range; do 
     if ! kill -0 ${pids[$i]} 2> /dev/null; then 
      echo "Done -- ${pids[$i]}" 
      unset pids[$i] 
      completed=$(expr $completed + 1) 
     fi 
    done 
    pids=("${pids[@]}") # Expunge nulls created by unset. 
    running=$((${#pids[@]})) 
    echo "#PIDS :"$running 
fi 
} 
# 
function addPid() { 
    desc=$1 
    pid=$2 
    echo " ${desc} - "$pid 
    pids=(${pids[@]} $pid) 
} 
######################################################################## 
# 
# Loop and report when job changes happen, 
# keep going until all are completed. 
# 
idx=0 
while [ $completed -lt ${ngrid} ] 
do 
# 
    if [ $running -lt $n ] && [ $idx -lt ${ngrid} ] 
    then 
#################################################################### 
# 
# submit a new process if less than n 
# are running and we haven't finished... 
# 
# get desc for process 
# 
     name="job_"${idx} 
# background execution 
     sleep 3 & 
     addPid $name $! 
     idx=$(expr $idx + 1) 
# 
#################################################################### 
# 
    fi 
# 
    checkPids 
# if something changes... 
    if [ ${running} -gt ${prunning} ] || \ 
     [ ${completed} -gt ${pcompleted} ] 
    then 
     remain=$(expr $ngrid - $completed) 
     echo " Running: "${running}" Submitted: "${idx}\ 
       " Completed: "$completed" Remaining: "$remain 
    fi 
# save counts to prev values 
    prunning=${running} 
    pcompleted=${completed} 
# 
    sleep 1 
# 
done 
# 
######################################################################## 

Test:

job_0 - 75257 
1 
Checking for pids: 75257 
#PIDS :1 
Running: 1 Submitted: 1 Completed: 0 Remaining: 8 
job_1 - 75262 
2 
Checking for pids: 75257 75262 
#PIDS :2 
Running: 2 Submitted: 2 Completed: 0 Remaining: 8 
job_2 - 75267 
3 
Checking for pids: 75257 75262 75267 
#PIDS :3 
Running: 3 Submitted: 3 Completed: 0 Remaining: 8 
3 
Checking for pids: 75257 75262 75267 
Done -- 75257 
#PIDS :2 
Running: 2 Submitted: 3 Completed: 1 Remaining: 7 
job_3 - 75277 
3 
Checking for pids: 75262 75267 75277 
Done -- 75262 
#PIDS :2 
Running: 2 Submitted: 4 Completed: 2 Remaining: 6 
job_4 - 75283 
3 
Checking for pids: 75267 75277 75283 
Done -- 75267 
#PIDS :2 
Running: 2 Submitted: 5 Completed: 3 Remaining: 5 
job_5 - 75289 
3 
Checking for pids: 75277 75283 75289 
#PIDS :3 
Running: 3 Submitted: 6 Completed: 3 Remaining: 5 
3 
Checking for pids: 75277 75283 75289 
Done -- 75277 
#PIDS :2 
Running: 2 Submitted: 6 Completed: 4 Remaining: 4 
job_6 - 75298 
3 
Checking for pids: 75283 75289 75298 
Done -- 75283 
#PIDS :2 
Running: 2 Submitted: 7 Completed: 5 Remaining: 3 
job_7 - 75304 
3 
Checking for pids: 75289 75298 75304 
Done -- 75289 
#PIDS :2 
Running: 2 Submitted: 8 Completed: 6 Remaining: 2 
2 
Checking for pids: 75298 75304 
#PIDS :2 
2 
Checking for pids: 75298 75304 
Done -- 75298 
#PIDS :1 
Running: 1 Submitted: 8 Completed: 7 Remaining: 1 
1 
Checking for pids: 75304 
Done -- 75304 
#PIDS :0 
Running: 0 Submitted: 8 Completed: 8 Remaining: 0 
Các vấn đề liên quan