สี่งานพร้อมกัน…ฉันจะทำอย่างไร


23

ฉันมีรูปภาพ PNG จำนวนหนึ่งอยู่ในไดเรกทอรี ฉันมีแอปพลิเคชันชื่อ pngout ที่ฉันเรียกใช้เพื่อบีบอัดรูปภาพเหล่านี้ แอปพลิเคชันนี้ถูกเรียกใช้โดยสคริปต์ที่ฉันทำ ปัญหาคือสคริปต์นี้ทำทีละครั้งสิ่งนี้:

FILES=(./*.png)
for f in  "${FILES[@]}"
do
        echo "Processing $f file..."
        # take action on each file. $f store current file name
        ./pngout -s0 $f R${f/\.\//}
done

การประมวลผลครั้งละหนึ่งไฟล์ใช้เวลานาน หลังจากเรียกใช้แอพนี้ฉันเห็นว่าซีพียูเพียง 10% ดังนั้นฉันจึงค้นพบว่าฉันสามารถแบ่งไฟล์เหล่านี้ออกเป็น 4 แบทช์แต่ละแบทช์ในไดเรกทอรีและไฟ 4 จากสี่หน้าต่างเทอร์มินัลสี่กระบวนการสี่ดังนั้นฉันมีสี่อินสแตนซ์ของสคริปต์ของฉันในเวลาเดียวกัน งานใช้เวลา 1/4 ของเวลา

ปัญหาที่สองคือฉันเสียเวลาในการแบ่งภาพและแบทช์และคัดลอกสคริปต์ไปยังสี่ไดเรกทอรีเปิด windows terminal 4, bla bla ...

ด้วยสคริปต์เดียวโดยไม่ต้องแบ่งอะไร

ฉันหมายถึงสองสิ่ง: อันดับแรกฉันจะเริ่มจากสคริปต์ทุบตีเริ่มกระบวนการเป็นพื้นหลังได้อย่างไร (เพียงเพิ่ม & ไปยังจุดสิ้นสุด) ข้อสอง: ฉันจะหยุดส่งงานไปยังพื้นหลังได้อย่างไรหลังจากส่งงานที่สี่และวางสคริปต์ให้รอจนกว่างานจะสิ้นสุด ฉันหมายถึงเพียงแค่ส่งงานใหม่ไปที่พื้นหลังเมื่องานหนึ่งจบลงโดยทำ 4 งานให้ขนานกันเสมอ ถ้าฉันไม่ทำอย่างนั้นลูปจะยิง zillions ของงานไปที่พื้นหลังและ CPU จะอุดตัน


คำตอบ:


33

หากคุณมีสำเนาxargsที่รองรับการทำงานแบบขนานด้วย-Pคุณสามารถทำได้

printf '%s\0' *.png | xargs -0 -I {} -P 4 ./pngout -s0 {} R{}

สำหรับแนวคิดอื่น Wooliki Bash wiki มีส่วนในบทความการจัดการกระบวนการอธิบายสิ่งที่คุณต้องการ


2
นอกจากนี้ยังมี "gnu parallel" และ "xjobs" ที่ออกแบบมาสำหรับกรณีนี้ ส่วนใหญ่มันเป็นเรื่องของรสนิยมที่คุณชอบ
wnoise

คุณช่วยอธิบายคำสั่งที่เสนอได้มั้ย ขอบคุณ!
Eugene S

1
@EugeneS คุณจะเจาะจงเกี่ยวกับส่วนใดเพิ่มเติมอีกเล็กน้อย printf รวบรวมไฟล์ png ทั้งหมดและส่งผ่านไพพ์ไปยัง xargs ซึ่งรวบรวมอาร์กิวเมนต์จากอินพุตมาตรฐานและรวมเข้ากับอาร์กิวเมนต์สำหรับpngoutคำสั่งที่ OP ต้องการรัน ตัวเลือกที่สำคัญคือ-P 4ซึ่งบอกให้ xargs ใช้คำสั่งพร้อมกันสูงสุด 4 คำสั่ง
jw013

2
ขออภัยที่ไม่แม่นยำ ฉันสนใจเป็นพิเศษว่าทำไมคุณถึงใช้printfฟังก์ชั่นที่นี่มากกว่าปกติls .. | grep .. *.png? นอกจากนี้ฉันสนใจxargsพารามิเตอร์ที่คุณใช้ ( -0และ-I{}) ขอบคุณ!
Eugene S

3
@EugeneS เพื่อความถูกต้องและความทนทานสูงสุด ชื่อไฟล์ไม่ได้เส้นและlsไม่สามารถนำมาใช้เพื่อแยกชื่อไฟล์ portably และปลอดภัย อักขระที่ปลอดภัยเท่านั้นที่จะใช้เพื่อกำหนดชื่อไฟล์คือ\0และ/เนื่องจากอักขระอื่นทุกตัวรวมถึง\nสามารถเป็นส่วนหนึ่งของชื่อไฟล์ได้ การprintfใช้\0เพื่อกำหนดขอบเขตชื่อไฟล์และการ-0แจ้งxargsของสิ่งนี้ การ-I{}บอกxargsให้แทนที่{}ด้วยอาร์กิวเมนต์
jw013

8

นอกจากโซลูชันที่เสนอแล้วคุณสามารถสร้าง makefile ที่อธิบายวิธีสร้างไฟล์บีบอัดจากไม่บีบอัดและใช้make -j 4เพื่อรัน 4 งานพร้อมกัน ปัญหาคือคุณจะต้องตั้งชื่อไฟล์ที่บีบอัดและไม่บีบอัดแตกต่างกันหรือเก็บไว้ในไดเรกทอรีอื่นมิฉะนั้นการเขียนกฎการทำแบบสมเหตุสมผลจะเป็นไปไม่ได้


7

หากคุณติดตั้งGNU Parallel http://www.gnu.org/software/parallel/คุณสามารถทำได้:

parallel ./pngout -s0 {} R{} ::: *.png

คุณสามารถติดตั้ง GNU Parallel ได้ง่ายๆโดย:

wget http://git.savannah.gnu.org/cgit/parallel.git/plain/src/parallel
chmod 755 parallel
cp parallel sem

ดูวิดีโอแนะนำสำหรับ GNU Parallel เพื่อเรียนรู้เพิ่มเติม: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1


5

ในการตอบคำถามสองข้อของคุณ:

  • ใช่การเพิ่ม & ท้ายบรรทัดจะแนะนำให้คุณเชลล์เริ่มกระบวนการพื้นหลัง
  • ใช้waitคำสั่งคุณสามารถขอให้เชลล์รอให้กระบวนการทั้งหมดในพื้นหลังเสร็จสิ้นก่อนที่จะดำเนินการต่อไป

นี่คือสคริปต์ที่ปรับเปลี่ยนเพื่อjใช้ในการติดตามจำนวนกระบวนการพื้นหลัง เมื่อNB_CONCURRENT_PROCESSESถึงแล้วสคริปต์จะรีเซ็ตjเป็น 0 และรอให้กระบวนการส่วนหลังทั้งหมดเสร็จสิ้นก่อนที่จะดำเนินการต่อ

files=(./*.png)
nb_concurrent_processes=4
j=0
for f in "${files[@]}"
do
        echo "Processing $f file..."
        # take action on each file. $f store current file name
        ./pngout -s0 "$f" R"${f/\.\//}" &
        ((++j == nb_concurrent_processes)) && { j=0; wait; }
done

1
สิ่งนี้จะรอกระบวนการสุดท้ายที่เกิดขึ้นพร้อมกันสี่กระบวนการและจากนั้นจะเริ่มชุดกระบวนการอีกสี่กระบวนการ บางทีเราควรสร้างอาร์เรย์ของ PID สี่ชุดจากนั้นรอ PID ที่ระบุเหล่านี้หรือไม่
นิลส์

เพียงเพื่ออธิบายการแก้ไขของฉันกับรหัส: (1) ตามสไตล์หลีกเลี่ยงชื่อตัวแปรตัวพิมพ์ใหญ่ทั้งหมดเนื่องจากอาจขัดแย้งกับตัวแปรเชลล์ภายใน (2) เพิ่มข้อความสำหรับ$fฯลฯ (3) ใช้[สำหรับสคริปต์ที่เข้ากันได้กับ POSIX แต่สำหรับการทุบตีบริสุทธิ์[[นั้นเป็นที่ต้องการเสมอ ในกรณี((นี้เหมาะสมกว่าสำหรับเลขคณิต
jw013
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.