Các vòng vỏ song song


11

Tôi muốn xử lý nhiều tệp và vì tôi đã ở đây một loạt các lõi nên tôi muốn làm song song:

for i in *.myfiles; do do_something $i `derived_params $i` other_params; done

Tôi biết một giải pháp Makefile nhưng các lệnh của tôi cần các đối số trong danh sách toàn cầu shell. Những gì tôi tìm thấy là:

> function pwait() {
>     while [ $(jobs -p | wc -l) -ge $1 ]; do
>         sleep 1
>     done
> }
>

Để sử dụng nó, tất cả những gì phải làm là đặt & sau các công việc và một cuộc gọi chờ, tham số đưa ra số lượng các quy trình song song:

> for i in *; do
>     do_something $i &
>     pwait 10
> done

Nhưng điều này không hoạt động rất tốt, ví dụ tôi đã thử nó với vòng lặp for để chuyển đổi nhiều tệp nhưng lại gây ra lỗi và hoàn thành công việc.

Tôi không thể tin rằng điều này vẫn chưa được thực hiện vì cuộc thảo luận về danh sách gửi thư zsh đã quá cũ. Vậy bạn có biết gì hơn không?


Tương tự với câu hỏi này: superuser.com/questions/153630/ Từ Xem xem kỹ thuật đó có hiệu quả với bạn không.
JRobert

Nó sẽ hữu ích nếu bạn đăng các thông báo lỗi.
Tạm dừng cho đến khi có thông báo mới.

@JRobert vâng tôi biết điều này nhưng điều này thực sự không hữu ích vì cách tiếp cận makefile sẽ không hiệu quả như tôi đã nói! @Dennis: Ok, đầu tiên tôi cho phép chạy một đỉnh bên cạnh hiển thị cho tôi nhiều hơn số lượng quy trình được chỉ định. Thứ hai nó không trở lại đúng dấu nhắc. Thứ ba mà tôi đã nói nó khiến các công việc hoàn tác là không đúng: Tôi chỉ đặt một chỉ báo echo "DONE"sau vòng lặp được thực thi trước khi các công việc đang hoạt động chưa kết thúc. => Điều này khiến tôi nghĩ rằng công việc chưa được thực hiện.
toán

Câu trả lời:


15

Makefile một giải pháp tốt cho vấn đề của bạn. Bạn có thể lập trình thực hiện song song này trong một trình bao, nhưng thật khó, như bạn đã nhận thấy. Việc thực hiện song song sẽ không chỉ đảm nhiệm các công việc bắt đầu và phát hiện sự chấm dứt của chúng, mà còn xử lý cân bằng tải, rất khó khăn.

Yêu cầu đối với Globing không phải là một trở ngại: có những triển khai thực hiện hỗ trợ nó. GNU make, có mở rộng ký tự đại diện như $(wildcard *.c)và truy cập shell, chẳng hạn như $(shell mycommand)(tra cứu các hàm trong GNU make bằng tay để biết thêm thông tin). Đây là mặc định maketrên Linux và có sẵn trên hầu hết các hệ thống khác. Đây là bộ xương Makefile mà bạn có thể thích ứng với nhu cầu của mình:

nguồn = $ (ký tự đại diện * .src)

tất cả: $ (nguồn: .src = .tgt)

% .tgt: $ .src
    do_s Something $ <$$ (origin_params $ <)> $ @

Chạy một cái gì đó như make -j4để thực hiện song song bốn công việc hoặc make -j -l3để giữ mức trung bình tải khoảng 3.


8

Tôi không chắc chắn những gì các đối số dẫn xuất của bạn là như thế nào. Nhưng với GNU Parallel http: // www.gnu.org/software/abul/ bạn có thể làm điều này để chạy một công việc trên mỗi lõi cpu:

find . | parallel -j+0 'a={}; name=${a##*/}; upper=$(echo "$name" | tr "[:lower:]" "[:upper:]");
   echo "$name - $upper"'

Nếu những gì bạn muốn lấy chỉ đơn giản là thay đổi .extension thì {.} Có thể có ích:

parallel -j+0 lame {} -o {.}.mp3 ::: *.wav

Xem video giới thiệu về GNU Parallel tại http://www.youtube.com/watch?v=OpaiGYxkSuQ


7

Sẽ không sử dụng waitlệnh của shell làm việc cho bạn?

for i in *
do
    do_something $i &
done
wait

Vòng lặp của bạn thực thi một công việc sau đó chờ nó, sau đó thực hiện công việc tiếp theo. Nếu những điều trên không phù hợp với bạn, thì bạn có thể làm việc tốt hơn nếu bạn di chuyển pwaitsau done.


không với 1 triệu tệp tôi sẽ có 1 triệu tiến trình đang chạy, hay tôi sai?
toán

1
@brubelsabs: Chà, nó sẽ cố gắng thực hiện một triệu quy trình. Bạn đã không nói trong câu hỏi của bạn có bao nhiêu tệp bạn cần xử lý. Tôi nghĩ rằng bạn cần sử dụng các forvòng lặp lồng nhau để hạn chế rằng: for file in *; do for i in {1..10}; do do_something "$i" & done; wait; done(chưa được kiểm tra) Điều đó nên thực hiện mười lần một lúc và đợi cho đến khi tất cả mười nhóm của mỗi nhóm được thực hiện trước khi bắt đầu mười vòng tiếp theo. Vòng lặp của bạn làm một lúc làm cho xe mô tô &. Xem câu hỏi mà JRobert liên kết đến cho các tùy chọn khác. Tìm kiếm trên Stack Overflow cho các câu hỏi khác tương tự như câu hỏi của bạn (và câu hỏi đó).
Tạm dừng cho đến khi có thông báo mới.

Nếu OP dự đoán một triệu tệp thì anh ta sẽ gặp vấn đề for i in *. Anh ta sẽ phải chuyển các đối số vào vòng lặp với một đường ống hoặc một cái gì đó. Sau đó, thay vì vòng lặp nội bộ, bạn có thể chạy bộ đếm tăng dần và chạy "micro-"wait"-s"mọi "$ ((i% 32))" -eq '0'

@DennisWilliamson: kết hợp waitvới vòng lặp bên trong hoạt động tốt với tôi. Cảm ơn!
Joel Purra

3

Tại sao không ai nhắc đến xargs?

Giả sử bạn có chính xác ba đối số,

for i in *.myfiles; do echo -n $i `derived_params $i` other_params; done | xargs -n 3 -P $PROCS do_something

Mặt khác, sử dụng một dấu phân cách (null là tiện dụng cho việc đó):

for i in *.myfiles; do echo -n $i `derived_params $i` other_params; echo -ne "\0"; done | xargs -0 -n 1 -P $PROCS do_something

EDIT: đối với ở trên, mỗi tham số nên được phân tách bằng ký tự null và sau đó số lượng tham số sẽ được chỉ định bằng xargs -n.


Có trong dự án của chúng tôi, ai đó đã có cùng ý tưởng và nó hoạt động rất tốt ngay cả trong Windows với MSys.
toán

0

Tôi đã thử một số câu trả lời. Họ làm cho kịch bản phức tạp hơn một chút so với mức cần thiết. Tuy nhiên, lý tưởng nhất là sử dụng parallelhoặc xargssẽ tốt hơn nếu các thao tác bên trong vòng lặp for phức tạp, có thể có vấn đề khi tạo một tệp lớn và dài để cung cấp song song. thay vào đó chúng ta có thể sử dụng nguồn như sau

# Create a test file 
$ cat test.txt
task_test 1
task_test 2

# Create a shell source file 
$ cat task.sh
task_test()
{
    echo $1
}

# use the source under bash -c 
$ cat test.txt | xargs -n1 -I{} bash -c 'source task.sh; {}'
1
2

Do đó, giải pháp cho vấn đề của bạn sẽ như thế nào

for i in *.myfiles; echo " do_something $i `derived_params $i` other_params
" >> commands.txt ; done

định nghĩa làm một cái gì đó như do_something.sh

do_something(){
process $1
echo $2 
whatever $3 

}

thực hiện với xarghoặcgnu parallel

   cat commands.txt | xargs -n1 -I{} -P8 bash -c 'source do_something.sh; {}'

Tôi giả định sự độc lập về chức năng của các lần lặp của for được ngụ ý.

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.