สำหรับฉันมันค่อนข้างง่ายจริง ๆ :
กระบวนการย่อยตัวเลือก:
subprocess
มีไว้สำหรับเรียกใช้ไฟล์ปฏิบัติการอื่น ๆ - โดยพื้นฐานแล้วจะเป็นกระดาษห่อหุ้มรอบ ๆos.fork()
และos.execve()
ด้วยการสนับสนุนท่อประปาที่เป็นทางเลือก (การตั้งค่า PIPE ไปยังและจากกระบวนการย่อยเห็นได้ชัดว่าคุณสามารถใช้กลไกการสื่อสารระหว่างกระบวนการ (IPC) อื่น ๆ เช่นซ็อกเก็ตหรือ Posix หรือ หน่วยความจำที่ใช้ร่วมกัน SysV แต่คุณจะถูก จำกัด เฉพาะอินเทอร์เฟซและช่อง IPC ที่รองรับโดยโปรแกรมที่คุณเรียกใช้
โดยทั่วไปเราใช้แบบsubprocess
ซิงโครนัส - เพียงแค่เรียกยูทิลิตีภายนอกบางตัวและอ่านเอาต์พุตหรือรอให้เสร็จสิ้น (อาจอ่านผลลัพธ์จากไฟล์ชั่วคราวหรือหลังจากโพสต์ไปยังฐานข้อมูลบางส่วน)
อย่างไรก็ตามเราสามารถสร้างกระบวนการย่อยหลายร้อยขั้นตอนและสำรวจความคิดเห็นได้ ยูทิลิตี้เองโปรดของฉันclasshไม่ตรงที่
ข้อเสียที่ใหญ่ที่สุดของsubprocess
โมดูลคือการรองรับ I / O โดยทั่วไปจะปิดกั้น มีร่างPEP-3145สำหรับแก้ไขใน Python 3.x เวอร์ชันอนาคตและasyncprocทางเลือก(คำเตือนที่นำไปสู่การดาวน์โหลดโดยตรงไม่ใช่เอกสารประเภทใด ๆ หรือ README) ฉันยังพบว่ามันค่อนข้างง่ายที่จะนำเข้าfcntl
และจัดการกับPopen
ตัวอธิบายไฟล์ PIPE ของคุณโดยตรง - แม้ว่าฉันจะไม่รู้ว่ามันพกพาไปยังแพลตฟอร์มที่ไม่ใช่ UNIX ได้หรือไม่
(อัปเดต: 7 สิงหาคม 2019: Python 3 รองรับกระบวนการย่อยayncio : asyncio Subprocesses )
subprocess
แทบจะไม่มีการรองรับการจัดการเหตุการณ์ ... แม้ว่าคุณจะสามารถใช้signal
โมดูลและสัญญาณ UNIX / Linux แบบเก่า ๆ - ฆ่ากระบวนการของคุณอย่างนุ่มนวลเหมือนที่เคยเป็นมา
multiprocessingตัวเลือก:
multiprocessing
มีไว้สำหรับการเรียกใช้ฟังก์ชันภายในโค้ด (Python) ที่มีอยู่ของคุณโดยรองรับการสื่อสารที่ยืดหยุ่นมากขึ้นระหว่างกระบวนการตระกูลนี้ โดยเฉพาะอย่างยิ่งคุณควรสร้างmultiprocessing
IPC ของคุณรอบ ๆQueue
อ็อบเจ็กต์ของโมดูลหากเป็นไปได้ แต่คุณยังสามารถใช้Event
อ็อบเจ็กต์และคุณสมบัติอื่น ๆ ได้อีกด้วย (ซึ่งบางส่วนน่าจะสร้างขึ้นจากmmap
การสนับสนุนบนแพลตฟอร์มที่การรองรับนั้นเพียงพอ)
multiprocessing
โมดูลของ Python มีจุดมุ่งหมายเพื่อจัดเตรียมอินเทอร์เฟซและคุณสมบัติที่คล้ายกัน มากthreading
ในขณะที่อนุญาตให้ CPython ปรับขนาดการประมวลผลของคุณระหว่าง CPU / คอร์หลายตัวแม้จะมี GIL (Global Interpreter Lock) ใช้ประโยชน์จากการล็อก SMP แบบละเอียดและความพยายามในการทำงานร่วมกันที่เกิดขึ้นโดยนักพัฒนาเคอร์เนลระบบปฏิบัติการของคุณ
เกลียวตัวเลือก:
threading
มีไว้สำหรับแอปพลิเคชันที่ค่อนข้างแคบซึ่งมีการเชื่อมต่อ I / O (ไม่จำเป็นต้องปรับขนาดในแกน CPU หลายตัว) และได้รับประโยชน์จากเวลาแฝงที่ต่ำมากและการเปลี่ยนค่าใช้จ่ายในการสลับเธรด (ที่มีหน่วยความจำหลักที่ใช้ร่วมกัน) เทียบกับกระบวนการ / การสลับบริบท บน Linux นี่เกือบจะเป็นชุดที่ว่างเปล่า (เวลาในการสลับกระบวนการของ Linux อยู่ใกล้กับเธรดสวิตช์มาก)
threading
ทนทุกข์ทรมานจากสองข้อเสียที่สำคัญในหลาม
แน่นอนว่าหนึ่งคือการใช้งานเฉพาะ - ส่วนใหญ่มีผลต่อ CPython นั่นคือ GIL โดยส่วนใหญ่โปรแกรม CPython ส่วนใหญ่จะไม่ได้รับประโยชน์จากความพร้อมใช้งานของซีพียู (คอร์) มากกว่าสองตัวและบ่อยครั้งที่ประสิทธิภาพจะได้รับผลกระทบจากข้อขัดแย้งในการล็อก GIL
ปัญหาที่ใหญ่กว่าซึ่งไม่ใช่เฉพาะการนำไปใช้งานคือเธรดใช้หน่วยความจำตัวจัดการสัญญาณตัวอธิบายไฟล์และทรัพยากรระบบปฏิบัติการอื่น ๆ ร่วมกัน ดังนั้นโปรแกรมเมอร์จึงต้องระมัดระวังอย่างมากเกี่ยวกับการล็อกอ็อบเจ็กต์การจัดการข้อยกเว้นและด้านอื่น ๆ ของโค้ดซึ่งทั้งละเอียดอ่อนและสามารถฆ่าหยุดหรือหยุดชะงักกระบวนการทั้งหมด (ชุดเธรด)
โดยการเปรียบเทียบmultiprocessing
โมเดลจะให้หน่วยความจำของตัวเองตัวอธิบายไฟล์ ฯลฯ ข้อผิดพลาดหรือข้อยกเว้นที่ไม่สามารถจัดการได้ในหนึ่งในนั้นจะฆ่าทรัพยากรนั้นเท่านั้นและการจัดการกับการหายไปของกระบวนการย่อยหรือพี่น้องอย่างมีประสิทธิภาพนั้นทำได้ง่ายกว่าการดีบักแยก และแก้ไขหรือแก้ไขปัญหาที่คล้ายกันในชุดข้อความ
- (หมายเหตุ: การใช้
threading
กับระบบ Python หลัก ๆ เช่นNumPyอาจได้รับผลกระทบจากการโต้แย้ง GIL น้อยกว่ารหัส Python ส่วนใหญ่ของคุณเองนั่นเป็นเพราะพวกเขาได้รับการออกแบบมาโดยเฉพาะเพื่อทำเช่นนั้นส่วนเนทีฟ / ไบนารีของ NumPy ตัวอย่างเช่นจะปล่อย GIL เมื่อปลอดภัย)
บิดตัวเลือก:
นอกจากนี้ยังเป็นที่น่าสังเกตว่าบิดข้อเสนอยังอีกทางเลือกหนึ่งซึ่งเป็นทั้งหรูหราและมากความท้าทายที่จะเข้าใจ โดยทั่วไปแล้วหากมีความเสี่ยงที่จะทำให้ง่ายขึ้นจนถึงจุดที่แฟน ๆ ของ Twisted อาจบุกบ้านของฉันด้วยโกยและคบเพลิง Twisted จะจัดเตรียมการทำงานร่วมกันหลายอย่างที่ขับเคลื่อนด้วยเหตุการณ์ภายในกระบวนการใด ๆ (เดียว)
เพื่อให้เข้าใจว่าสิ่งนี้เป็นไปได้อย่างไรควรอ่านเกี่ยวกับคุณสมบัติของselect()
(ซึ่งสามารถสร้างขึ้นจากการเลือก ()หรือแบบสำรวจ ()หรือการเรียกระบบปฏิบัติการที่คล้ายกัน) โดยพื้นฐานแล้วทั้งหมดนี้เกิดจากความสามารถในการร้องขอให้ระบบปฏิบัติการเข้าสู่โหมดสลีปเพื่อรอดำเนินกิจกรรมใด ๆ ในรายการตัวอธิบายไฟล์หรือการหมดเวลา
การปลุกจากการเรียกแต่ละครั้งถึงselect()
เป็นเหตุการณ์หนึ่งซึ่งเกี่ยวข้องกับอินพุตที่พร้อมใช้งาน (อ่านได้) บนซ็อกเก็ตหรือตัวอธิบายไฟล์จำนวนหนึ่งหรือพื้นที่บัฟเฟอร์ที่พร้อมใช้งานบนตัวอธิบายหรือซ็อกเก็ตอื่น ๆ (เขียนได้) เงื่อนไขพิเศษบางประการ (TCP แพ็กเก็ต PUSH ที่ไม่อยู่ในวงดนตรีเป็นต้น) หรือ TIMEOUT
ดังนั้นแบบจำลองการเขียนโปรแกรม Twisted จึงถูกสร้างขึ้นโดยใช้การจัดการเหตุการณ์เหล่านี้จากนั้นวนซ้ำบนตัวจัดการ "หลัก" ที่เป็นผลลัพธ์ทำให้สามารถส่งเหตุการณ์ไปยังตัวจัดการของคุณได้
โดยส่วนตัวฉันคิดว่าชื่อTwistedเป็นตัวกระตุ้นให้เกิดรูปแบบการเขียนโปรแกรม ... เนื่องจากแนวทางของคุณในการแก้ปัญหาต้องเป็น "บิด" จากภายในสู่ภายนอก แทนที่จะคิดโปรแกรมของคุณเป็นชุดของการดำเนินการกับข้อมูลอินพุตและเอาต์พุตหรือผลลัพธ์คุณกำลังเขียนโปรแกรมของคุณเป็นบริการหรือดีมอนและกำหนดวิธีที่จะตอบสนองต่อเหตุการณ์ต่างๆ (ในความเป็นจริงแกนหลัก "ลูปหลัก" ของโปรแกรม Twisted คือ (โดยปกติคือเสมอ?) กreactor()
)
ความท้าทายที่สำคัญในการใช้บิดเกี่ยวข้องกับการบิดใจรอบแบบจำลองเหตุการณ์ที่ขับเคลื่อนด้วยและยังละทิ้งการใช้ห้องสมุดชั้นใด ๆ หรือชุดเครื่องมือที่ไม่ได้เขียนขึ้นเพื่อร่วมดำเนินการภายในกรอบบิด นี่คือเหตุผลที่ Twisted จัดหาโมดูลของตัวเองสำหรับการจัดการโปรโตคอล SSH สำหรับคำสาปและฟังก์ชั่นกระบวนการย่อย / Popen ของตัวเองรวมถึงโมดูลและตัวจัดการโปรโตคอลอื่น ๆ อีกมากมายซึ่งในตอนแรกบลัชออนดูเหมือนจะทำซ้ำสิ่งต่างๆในไลบรารีมาตรฐาน Python
ฉันคิดว่าการทำความเข้าใจ Twisted ในระดับแนวคิดมีประโยชน์แม้ว่าคุณจะไม่เคยตั้งใจใช้ก็ตาม อาจให้ข้อมูลเชิงลึกเกี่ยวกับประสิทธิภาพการแข่งขันและการจัดการเหตุการณ์ในเธรดการประมวลผลหลายกระบวนการและแม้แต่การจัดการกระบวนการย่อยรวมทั้งการประมวลผลแบบกระจายที่คุณดำเนินการ
( หมายเหตุ: Python 3.x เวอร์ชันใหม่กว่าจะรวมคุณสมบัติ asyncio (asynchronous I / O) เช่นasync def , @ async.coroutine decorator และawait keyword และผลตอบแทนจากการสนับสนุนในอนาคตทั้งหมดนี้มีความคล้ายคลึงกับบิดจากมุมมองของกระบวนการ (การทำงานหลายอย่างร่วมกัน) (สำหรับสถานะปัจจุบันของการสนับสนุน Twisted สำหรับ Python 3 โปรดดู: https://twistedmatrix.com/documents/current/core/howto/python3.html )
กระจายตัวเลือก:
อีกหนึ่งขอบเขตของการประมวลผลที่คุณไม่ได้ถามถึง แต่สิ่งที่ควรค่าแก่การพิจารณาคือการประมวลผลแบบกระจาย มีเครื่องมือและเฟรมเวิร์ก Python มากมายสำหรับการประมวลผลแบบกระจายและการคำนวณแบบขนาน โดยส่วนตัวแล้วฉันคิดว่าวิธีที่ง่ายที่สุดในการใช้คือสิ่งที่มักถูกมองว่าอยู่ในพื้นที่นั้นน้อยที่สุด
มันเป็นเรื่องเล็ก ๆ น้อยเกือบจะสร้างประมวลผลแบบกระจายทั่วRedis สามารถใช้ที่เก็บคีย์ทั้งหมดเพื่อจัดเก็บหน่วยงานและผลลัพธ์ Redis LIST สามารถใช้เป็นQueue()
เหมือนวัตถุและการรองรับ PUB / SUB สามารถใช้สำหรับการEvent
จัดการที่เหมือนกันได้ คุณสามารถแฮชคีย์และใช้ค่าที่จำลองแบบข้ามกลุ่มอินสแตนซ์ Redis แบบหลวม ๆ เพื่อจัดเก็บโทโพโลยีและการแมปแฮชโทเค็นเพื่อจัดเตรียมการแฮชที่สอดคล้องกันและการล้มเหลวสำหรับการปรับขนาดเกินขีดความสามารถของอินสแตนซ์เดียวสำหรับการประสานงานคนงานของคุณ และข้อมูลการจัดกลุ่ม (ดอง JSON BSON หรือ YAML)
แน่นอนว่าเมื่อคุณเริ่มสร้างสเกลที่ใหญ่ขึ้นและโซลูชันที่ซับซ้อนมากขึ้นรอบ ๆ Redis คุณกำลังนำคุณสมบัติหลายอย่างที่ได้รับการแก้ไขไปแล้วมาใช้ใหม่โดยใช้Celery , Apache SparkและHadoop , Zookeeper , etcd , Cassandraและอื่น ๆ ทั้งหมดนี้มีโมดูลสำหรับ Python ในการเข้าถึงบริการของตน
[Update: คู่ของทรัพยากรสำหรับการพิจารณาถ้าคุณกำลังพิจารณาหลามสำหรับคอมพิวเตอร์อย่างเข้มข้นในระบบการกระจาย: ขนาน IPythonและPySpark แม้ว่าระบบเหล่านี้จะเป็นระบบคอมพิวเตอร์แบบกระจายจุดประสงค์ทั่วไป แต่ระบบย่อยเหล่านี้สามารถเข้าถึงได้และเป็นที่นิยมโดยเฉพาะวิทยาศาสตร์ข้อมูลและการวิเคราะห์]
สรุป
คุณมีขอบเขตของทางเลือกในการประมวลผลสำหรับ Python ตั้งแต่เธรดเดี่ยวพร้อมการเรียกใช้แบบซิงโครนัสอย่างง่ายไปจนถึงกระบวนการย่อยกลุ่มของกระบวนการย่อยที่โพรเซสเธรดและการประมวลผลหลายกระบวนการแบบมัลติทาสก์แบบร่วมมือที่ขับเคลื่อนด้วยเหตุการณ์และออกไปจนถึงการประมวลผลแบบกระจาย