- 1 1. Python Thread คืออะไร?
- 2 2. การทำความเข้าใจ Global Interpreter Lock (GIL) ใน Python
- 3 3. วิธีใช้โมดูล threading เบื้องต้นใน Python
- 4 4. การสืบทอดคลาส Thread เพื่อสร้าง Thread
- 5 การทำ Subclass ของ Thread
- 6 5. ความปลอดภัยและการซิงโครไนซ์ของ Thread
- 7 6. Thread กับงาน I/O-bound และ CPU-bound
- 8 7. การจัดการ Thread
- 9 8. การเปรียบเทียบ Thread กับ multiprocessing
- 10 9. แนวทางปฏิบัติที่ดีที่สุดในการใช้โมดูล threading ของ Python
- 11 10. สรุป
1. Python Thread คืออะไร?
Thread ใน Python คือกลไกที่ช่วยให้โปรแกรมสามารถทำงานหลาย ๆ งานพร้อมกันได้ภายในเวลาเดียวกัน การใช้ Thread ทำให้บางส่วนของโปรแกรมสามารถรันไปพร้อมกันโดยไม่ต้องรออีกส่วนหนึ่ง จึงช่วยเพิ่มประสิทธิภาพในการทำงาน ใน Python เราสามารถสร้างและจัดการ Thread ได้โดยใช้โมดูล threading
.
แนวคิดพื้นฐานของ Thread
Thread คือหน่วยการทำงานขนาดเล็กที่รันอยู่ภายใน Process เดียวกัน ภายในหนึ่ง Process สามารถมีหลาย Thread ที่ทำงานอิสระต่อกันได้ ซึ่งช่วยให้โปรแกรมรองรับการทำงานแบบขนาน (concurrent processing) ได้เป็นอย่างดี โดยเฉพาะงานที่เกี่ยวข้องกับ I/O (เช่น การอ่านเขียนไฟล์ หรือการสื่อสารผ่านเครือข่าย) และการปรับปรุงการตอบสนองของ UI
ตัวอย่างการใช้ Thread ใน Python
เช่น ในการสร้าง Web Scraping Tool เราสามารถเข้าถึงหลายเว็บเพจพร้อมกันเพื่อลดเวลาในการประมวลผลทั้งหมดได้ หรือในแอปพลิเคชันที่ต้องประมวลผลข้อมูลแบบเรียลไทม์ เราสามารถอัปเดตข้อมูลใน Background โดยไม่รบกวนการทำงานหลักของโปรแกรม

2. การทำความเข้าใจ Global Interpreter Lock (GIL) ใน Python
ใน Python Thread มีแนวคิดสำคัญที่เรียกว่า Global Interpreter Lock (GIL) ซึ่งเป็นกลไกที่จำกัดให้ Python Interpreter สามารถรันได้ทีละหนึ่ง Thread เท่านั้น
ผลกระทบของ GIL
GIL ป้องกันไม่ให้หลาย Thread ทำงานพร้อมกันในเวลาเดียวกันเพื่อรักษาความสอดคล้องในการจัดการหน่วยความจำของ Process เดียวกัน อย่างไรก็ตาม ข้อจำกัดนี้ทำให้การประมวลผลที่ใช้ CPU หนัก (CPU-bound tasks) ไม่ได้ประโยชน์จาก Thread เท่าที่ควร ตัวอย่างเช่น แม้จะสร้างหลาย Thread เพื่อคำนวณที่ซับซ้อน แต่เนื่องจาก GIL จะอนุญาตให้รันทีละ Thread เท่านั้น จึงไม่สามารถเพิ่มประสิทธิภาพได้ตามที่คาดหวัง
วิธีการหลีกเลี่ยง GIL
เพื่อหลีกเลี่ยงข้อจำกัดของ GIL เราสามารถใช้โมดูล multiprocessing
ซึ่งจะสร้าง Process แยกออกมา โดยแต่ละ Process จะมี Python Interpreter ของตัวเอง จึงสามารถทำงานแบบขนานได้จริงโดยไม่ติดข้อจำกัดของ GIL
3. วิธีใช้โมดูล threading
เบื้องต้นใน Python
โมดูล threading
เป็นไลบรารีมาตรฐานใน Python ที่ช่วยให้เราสามารถสร้างและจัดการ Thread ได้ง่าย ต่อไปนี้เป็นวิธีการใช้งานเบื้องต้น
การสร้างและรัน Thread
เราสามารถสร้าง Thread ได้โดยใช้คลาส threading.Thread
เช่นตัวอย่างนี้:
import threading
import time
def my_function():
time.sleep(2)
print("Thread executed")
# สร้าง Thread
thread = threading.Thread(target=my_function)
# เริ่มทำงาน Thread
thread.start()
# รอให้ Thread ทำงานเสร็จ
thread.join()
print("Main thread completed")
ในโค้ดนี้ เราได้สร้าง Thread ใหม่เพื่อรัน my_function
แบบ asynchronous
การซิงโครไนซ์ของ Thread
เพื่อรอให้ Thread ทำงานเสร็จ เราสามารถใช้เมธอด join()
เมธอดนี้จะหยุดการทำงานของ Main Thread จนกว่า Thread เป้าหมายจะเสร็จสิ้น ทำให้สามารถควบคุมการทำงานให้เป็นลำดับได้

4. การสืบทอดคลาส Thread
เพื่อสร้าง Thread
เราสามารถสืบทอด (subclass) คลาส threading.Thread
เพื่อปรับแต่งการทำงานของ Thread ได้อย่างยืดหยุ่นมากขึ้น
การทำ Subclass ของ Thread
เราสามารถสร้างคลาสใหม่จาก Thread
และ override เมธอด run()
ได้ดังนี้:
import threading
import time
class MyThread(threading.Thread):
def run(self):
time.sleep(2)
print("Custom thread executed")
# สร้างและรัน Thread แบบ Custom
thread = MyThread()
thread.start()
thread.join()
print("Main thread completed")
ข้อดีของการทำ Subclass
การทำ Subclass ช่วยให้เราสามารถห่อหุ้ม (encapsulate) เนื้อหาของการทำงานใน Thread ทำให้โค้ดสามารถนำกลับมาใช้ใหม่ได้ง่าย และยังสามารถจัดการข้อมูลเฉพาะสำหรับแต่ละ Thread ได้สะดวก
5. ความปลอดภัยและการซิงโครไนซ์ของ Thread
เมื่อหลาย Thread เข้าถึงทรัพยากรร่วมกัน จำเป็นต้องมีการซิงโครไนซ์เพื่อรักษาความถูกต้องของข้อมูล
Race Condition
Race Condition คือสถานการณ์ที่หลาย Thread เข้ามาแก้ไขทรัพยากรเดียวกันในเวลาเดียวกัน ส่งผลให้ผลลัพธ์ไม่เป็นไปตามที่คาด ตัวอย่างเช่น หากหลาย Thread พยายามเพิ่มค่าตัวแปร counter พร้อมกัน โดยไม่มีการควบคุม อาจทำให้ค่าที่ได้ไม่ถูกต้อง
การซิงโครไนซ์ด้วย Lock
โมดูล threading
มีออบเจกต์ Lock
สำหรับควบคุมการเข้าถึงทรัพยากร โดยอนุญาตให้ Thread เดียวทำงานกับทรัพยากรนั้นได้ในช่วงเวลาเดียว
import threading
counter = 0
lock = threading.Lock()
def increment_counter():
global counter
with lock:
counter += 1
threads = []
for _ in range(100):
thread = threading.Thread(target=increment_counter)
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
print("Final counter value:", counter)
ในตัวอย่างนี้ ตัวแปร counter จะถูกอัปเดตอย่างถูกต้องเพราะใช้ with lock

6. Thread กับงาน I/O-bound และ CPU-bound
Thread มีประสิทธิภาพสูงเป็นพิเศษเมื่อใช้งานกับงานประเภท I/O-bound (เช่น การอ่านเขียนไฟล์ หรือการเชื่อมต่อเครือข่าย)
ข้อดีของ Thread ในงาน I/O-bound
งาน I/O-bound มักใช้เวลารอคอย (waiting time) สูง เช่น การรออ่านไฟล์หรือรอการตอบกลับจากเครือข่าย การใช้ Thread จะช่วยให้สามารถทำงานอื่น ๆ ไปพร้อมกันในระหว่างรอได้ จึงเพิ่มประสิทธิภาพโดยรวมของโปรแกรม ตัวอย่างเช่น ใช้ Thread หนึ่งสำหรับการเขียนไฟล์ และอีก Thread หนึ่งสำหรับการสื่อสารเครือข่าย
งาน CPU-bound และการใช้ multiprocessing
สำหรับงานที่ใช้ CPU หนัก (เช่น การคำนวณเชิงตัวเลข หรือการประมวลผลข้อมูล) ควรใช้โมดูล multiprocessing
แทน threading
เนื่องจาก multiprocessing
ไม่ได้รับผลกระทบจาก GIL และสามารถใช้ประโยชน์จาก CPU หลายคอร์เพื่อเพิ่มประสิทธิภาพได้
7. การจัดการ Thread
ต่อไปนี้คือเทคนิคในการจัดการ Thread ใน Python อย่างมีประสิทธิภาพ
การตั้งชื่อและระบุตัวตนของ Thread
เราสามารถตั้งชื่อ Thread ได้โดยใช้พารามิเตอร์ name
ของ threading.Thread
ซึ่งช่วยให้ดีบักและตรวจสอบ log ได้ง่ายขึ้น
import threading
def task():
print(f"Thread {threading.current_thread().name} is running")
thread1 = threading.Thread(target=task, name="Thread1")
thread2 = threading.Thread(target=task, name="Thread2")
thread1.start()
thread2.start()
การตรวจสอบสถานะของ Thread
เราสามารถตรวจสอบว่า Thread กำลังทำงานอยู่หรือไม่โดยใช้เมธอด is_alive()
ซึ่งจะคืนค่า True
หาก Thread ยังทำงานอยู่ และ False
หากเสร็จสิ้นแล้ว
import threading
import time
def task():
time.sleep(1)
print("Task completed")
thread = threading.Thread(target=task)
thread.start()
# ตรวจสอบสถานะ Thread
if thread.is_alive():
print("Thread is still running")
else:
print("Thread has finished")
การหยุด Thread
ใน Python โมดูล threading
ไม่มีวิธีการหยุด Thread โดยตรง เนื่องจากการหยุดแบบบังคับอาจก่อให้เกิดปัญหาความไม่สอดคล้องของข้อมูลและการจัดการทรัพยากร วิธีที่ปลอดภัยคือใช้ flag หรือเงื่อนไขเพื่อควบคุมการหยุด
import threading
import time
stop_thread = False
def task():
while not stop_thread:
print("Thread is running")
time.sleep(1)
thread = threading.Thread(target=task)
thread.start()
# รอ 5 วินาทีแล้วหยุด
time.sleep(5)
stop_thread = True
thread.join()
print("Thread has been stopped")

8. การเปรียบเทียบ Thread กับ multiprocessing
การเข้าใจความแตกต่างระหว่าง Thread และ Process จะช่วยให้เลือกใช้งานได้อย่างเหมาะสม
ข้อดีและข้อเสียของ Thread
Thread มีน้ำหนักเบา ใช้หน่วยความจำร่วมกันใน Process เดียว จึงมี overhead น้อย เหมาะกับงาน I/O-bound แต่สำหรับงาน CPU-bound จะถูกจำกัดด้วย GIL
ข้อดีของโมดูล multiprocessing
โมดูล multiprocessing
ใช้ Process แยกแต่ละตัวที่มี Python Interpreter ของตัวเอง ทำให้สามารถใช้ CPU ได้หลายคอร์พร้อมกันโดยไม่ติด GIL ซึ่งมีข้อได้เปรียบสำหรับงาน CPU-bound แต่การแชร์ข้อมูลระหว่าง Process ต้องใช้ Pipe หรือ Queue ซึ่งมี overhead มากกว่า Thread
แนวทางการเลือกใช้งาน
- ควรใช้ Thread: สำหรับงาน I/O-bound เช่น การจัดการไฟล์ การสื่อสารเครือข่าย หรือการปรับปรุงความตอบสนองของ GUI
- ควรใช้
multiprocessing
: สำหรับงาน CPU-bound หรือการประมวลผลแบบขนานที่ซับซ้อน ซึ่งต้องการหลีกเลี่ยงข้อจำกัดของ GIL
9. แนวทางปฏิบัติที่ดีที่สุดในการใช้โมดูล threading
ของ Python
ในการเขียนโปรแกรมแบบ Multi-thread ควรปฏิบัติตามแนวทางที่ดีเพื่อให้โค้ดมีความเสถียรและดีบักได้ง่าย
การหยุด Thread อย่างปลอดภัย
ควรหลีกเลี่ยงการบังคับหยุด Thread โดยตรง แต่ควรใช้ flag หรือเงื่อนไขเพื่อหยุดการทำงานแทน และหาก Thread ใช้ทรัพยากร ควรแน่ใจว่ามีการคืนค่า (release) ทรัพยากรนั้นเสมอ
การป้องกัน Deadlock
เมื่อใช้ Lock เพื่อซิงโครไนซ์ Thread จำเป็นต้องระวัง Deadlock โดยทำตามแนวทางต่อไปนี้:
- กำหนดลำดับการได้มาของ Lock และทำตามอย่างสม่ำเสมอ
- ใช้ Lock เฉพาะในช่วงเวลาที่จำเป็น
- ใช้คำสั่ง
with
เพื่อให้การปลด Lock เป็นไปโดยอัตโนมัติ
การดีบักและการใช้ Log
โปรแกรมที่ใช้ Thread มักดีบักได้ยาก ดังนั้นควรใช้โมดูล logging
เพื่อติดตามการทำงานของแต่ละ Thread ทำให้หาสาเหตุของปัญหาได้ง่ายขึ้น
import threading
import logging
logging.basicConfig(level=logging.DEBUG, format='%(threadName)s: %(message)s')
def task():
logging.debug('Starting')
logging.debug('Exiting')
thread = threading.Thread(target=task, name='MyThread')
thread.start()

10. สรุป
โมดูล threading
ของ Python เป็นเครื่องมือทรงพลังที่ช่วยให้โปรแกรมทำงานแบบขนานได้ บทความนี้ได้อธิบายตั้งแต่แนวคิดพื้นฐาน ผลกระทบของ GIL การเลือกใช้ระหว่าง Thread และ multiprocessing
ไปจนถึงแนวทางปฏิบัติที่ดีที่สุด
Thread เหมาะอย่างยิ่งสำหรับงาน I/O-bound แต่ผู้พัฒนาควรเข้าใจข้อจำกัดของ GIL และเลือกใช้ให้เหมาะสม นอกจากนี้การจัดการ Thread อย่างปลอดภัยและเป็นระบบจะช่วยเพิ่มทั้งประสิทธิภาพและความน่าเชื่อถือของโปรแกรม
หากคุณต้องการเรียนรู้เพิ่มเติมเกี่ยวกับการเขียนโปรแกรมแบบ Multi-thread และ Concurrency สามารถศึกษาได้จากเอกสารทางการของ Python และหนังสือเฉพาะทางเพื่อเข้าใจเชิงลึกมากยิ่งขึ้น