1. บทนำ
Python เป็นภาษาการเขียนโปรแกรมที่ใช้งานได้หลากหลาย โดยมีประสิทธิภาพสูงเป็นพิเศษในด้านการประมวลผลข้อมูล การเรียนรู้ของเครื่อง และการพัฒนาเว็บ ในบรรดาเครื่องมือเหล่านี้ โมดูล multiprocessing ถือเป็นไลบรารีสำคัญที่ช่วยให้สามารถทำงานแบบขนานได้ บทความนี้จะแนะนำตั้งแต่พื้นฐานไปจนถึงการประยุกต์ใช้งานของโมดูล multiprocessing ใน Python พร้อมคำอธิบายที่เข้าใจง่ายและเทคนิคเชิงปฏิบัติในการดึงประสิทธิภาพสูงสุดออกมา
2. multiprocessing คืออะไร?
2.1 ความจำเป็นของการประมวลผลแบบขนาน
โดยปกติ Python ทำงานแบบซิงเกิลเธรด แต่เมื่อเจอกับงานที่ซับซ้อนหรือข้อมูลจำนวนมาก ความเร็วในการประมวลผลอาจมีข้อจำกัด การใช้การประมวลผลแบบขนานช่วยให้สามารถทำหลายงานได้พร้อมกัน ใช้ CPU ทุกคอร์ให้เต็มประสิทธิภาพ และลดเวลาในการทำงาน โมดูล multiprocessing สามารถหลีกเลี่ยง GIL (Global Interpreter Lock) ของ Python และใช้หลายโปรเซสเพื่อให้การทำงานแบบขนานที่แท้จริงเกิดขึ้นได้
2.2 ความแตกต่างกับซิงเกิลเธรด
ในการทำงานแบบซิงเกิลเธรด โปรเซสเดียวจะทำงานแต่ละงานตามลำดับ แต่ใน มัลติโปรเซส หลายโปรเซสสามารถทำงานพร้อมกันได้ สิ่งนี้ช่วยให้ประสิทธิภาพดีขึ้น โดยเฉพาะกับงานที่ใช้ CPU หนัก เช่น การคำนวณเชิงตัวเลขขนาดใหญ่หรือการวิเคราะห์ข้อมูล
3. ไวยากรณ์พื้นฐานของโมดูล multiprocessing
3.1 วิธีใช้คลาส Process
พื้นฐานของโมดูล multiprocessing
คือการใช้ คลาส Process ที่ช่วยสร้างโปรเซสใหม่และทำงานแบบขนานได้ง่าย
import multiprocessing
def worker_function():
print("โปรเซสใหม่กำลังทำงาน")
if __name__ == "__main__":
process = multiprocessing.Process(target=worker_function)
process.start()
process.join()
ในโค้ดนี้ worker_function
จะถูกรันในโปรเซสใหม่ start()
ใช้ในการเริ่มโปรเซส และ join()
ใช้รอจนกว่าโปรเซสจะทำงานเสร็จ
3.2 วิธีส่งอาร์กิวเมนต์ไปยังโปรเซส
สามารถส่งอาร์กิวเมนต์ไปยังโปรเซสได้โดยใช้พารามิเตอร์ args
ตัวอย่างด้านล่างจะส่งค่าไปยังฟังก์ชัน worker
:
def worker(number):
print(f'Worker {number} กำลังทำงาน')
if __name__ == "__main__":
process = multiprocessing.Process(target=worker, args=(5,))
process.start()
process.join()
ด้วยวิธีนี้สามารถส่งข้อมูลแบบไดนามิกให้โปรเซสและทำงานขนานได้
4. การแชร์ข้อมูลและการซิงโครไนซ์
4.1 การใช้หน่วยความจำร่วมในการแชร์ข้อมูล
ในมัลติโปรเซส สามารถแชร์ข้อมูลอย่างปลอดภัยระหว่างโปรเซสได้โดยใช้ Value หรือ Array ซึ่งทำหน้าที่เป็น shared memory object ที่หลายโปรเซสเข้าถึงพร้อมกันได้
import multiprocessing
def increment_value(shared_value):
with shared_value.get_lock():
shared_value.value += 1
if __name__ == "__main__":
shared_value = multiprocessing.Value('i', 0)
processes = [multiprocessing.Process(target=increment_value, args=(shared_value,)) for _ in range(5)]
for process in processes:
process.start()
for process in processes:
process.join()
print(f'ค่าที่ได้สุดท้าย: {shared_value.value}')
โค้ดด้านบนแสดงตัวอย่างที่ 5 โปรเซสทำการเพิ่มค่าของตัวเลขใน shared memory พร้อมกัน โดยใช้ get_lock()
เพื่อป้องกันการชนกันของข้อมูล
4.2 การใช้ Lock เพื่อป้องกันการชนกันของข้อมูล
เมื่อหลายโปรเซสทำงานกับข้อมูลพร้อมกัน จำเป็นต้องใช้ กลไก Lock เพื่อป้องกันการชนกันของข้อมูล การใช้ Lock
จะทำให้มั่นใจได้ว่าการเข้าถึงข้อมูลถูกซิงโครไนซ์

5. การกระจายงานด้วย Process Pool
5.1 การใช้คลาส Pool
การใช้ Pool
ช่วยกระจายงานหลายอย่างให้กับหลายโปรเซสเพื่อทำงานพร้อมกัน เหมาะสำหรับงานที่ต้องประมวลผลข้อมูลจำนวนมาก
from multiprocessing import Pool
def square(x):
return x * x
if __name__ == "__main__":
with Pool(4) as pool:
results = pool.map(square, range(10))
print(results)
โค้ดนี้ทำการคำนวณค่ากำลังสองของตัวเลขในลิสต์ โดยแบ่งไปให้ 4 โปรเซสทำงานพร้อมกัน การใช้ map()
ทำให้การกระจายงานไปยังโปรเซสเป็นเรื่องง่าย
แผนภาพ: การกระจายงานด้วย Pool

5.2 ตัวอย่างการใช้ starmap
ด้วย starmap()
สามารถทำงานแบบขนานกับฟังก์ชันที่มีหลายอาร์กิวเมนต์ ตัวอย่างเช่น:
def multiply(x, y):
return x * y
if __name__ == "__main__":
with Pool(4) as pool:
results = pool.starmap(multiply, [(1, 2), (3, 4), (5, 6), (7, 8)])
print(results)
6. การใช้ทรัพยากร CPU ให้มีประสิทธิภาพสูงสุด
6.1 การใช้ cpu_count()
เพื่อปรับจำนวนโปรเซส
ด้วย multiprocessing.cpu_count()
สามารถตรวจสอบจำนวนคอร์ของระบบและกำหนดจำนวนโปรเซสได้อย่างเหมาะสม เพื่อหลีกเลี่ยงการสร้างโปรเซสมากเกินไป
from multiprocessing import Pool, cpu_count
if __name__ == "__main__":
with Pool(cpu_count() - 1) as pool:
results = pool.map(square, range(100))
print(results)
6.2 การใช้ทรัพยากรระบบอย่างมีประสิทธิภาพ
ควรหลีกเลี่ยงการใช้ CPU ทุกคอร์ โดยเว้นไว้ 1 คอร์เพื่อไม่ให้กระทบต่อการทำงานอื่นของระบบ
7. กรณีการใช้งานจริงและแนวทางปฏิบัติที่ดีที่สุด
7.1 ตัวอย่างการใช้งานจริง
การใช้ multiprocessing มีประโยชน์ในสถานการณ์ เช่น:
- การประมวลผลข้อมูลขนาดใหญ่: อ่านและประมวลผลหลายไฟล์พร้อมกัน
- การฝึกโมเดล Machine Learning แบบขนาน: รันการเทรนโมเดลหลายโปรเซสพร้อมกันเพื่อลดเวลา
- การทำ Web Crawling: ดึงข้อมูลจากหลายเพจพร้อมกันอย่างมีประสิทธิภาพ
7.2 แนวทางปฏิบัติที่ดีที่สุด
- การจัดสรรทรัพยากรอย่างเหมาะสม: กำหนดจำนวนโปรเซสตามจำนวนคอร์จริง
- การใช้ Debug และ Logging: ใช้
logging
เพื่อติดตามสถานะของโปรเซสและจัดการข้อผิดพลาด
import logging
import multiprocessing
def worker_function():
logging.info(f'โปรเซส {multiprocessing.current_process().name} เริ่มทำงาน')
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
process = multiprocessing.Process(target=worker_function, name='Worker1')
process.start()
process.join()
โค้ดนี้บันทึกการทำงานของแต่ละโปรเซสด้วย logging
เพื่อสามารถตรวจสอบย้อนหลังได้
- การจัดการข้อผิดพลาด: เนื่องจากมีหลายโปรเซสทำงานพร้อมกัน ต้องมีการใช้ try-except เพื่อจัดการข้อผิดพลาดไม่ให้กระทบกับโปรเซสอื่น

8. สรุป
บทความนี้ได้อธิบายการใช้โมดูล multiprocessing ของ Python เพื่อเพิ่มประสิทธิภาพในการทำงานแบบขนาน ตั้งแต่พื้นฐานการใช้ คลาส Process การแชร์ข้อมูล การกระจายงานด้วย Process Pool ไปจนถึงกรณีการใช้งานจริงและแนวทางปฏิบัติที่ดีที่สุด
การใช้การประมวลผลแบบขนานอย่างถูกต้องช่วยให้สามารถจัดการงานขนาดใหญ่ได้ดีขึ้น เช่น การประมวลผลข้อมูลจำนวนมาก การฝึกโมเดล Machine Learning และการ Web Crawling โมดูล multiprocessing จึงเป็นเครื่องมือสำคัญที่ช่วยเพิ่มประสิทธิภาพของ Python ได้อย่างมหาศาล
ขอแนะนำให้นำความรู้เหล่านี้ไปปรับใช้กับงานพัฒนาในชีวิตประจำวันเพื่อให้ได้ผลลัพธ์ที่ดีที่สุด