เธรดและ GIL
ทำความเข้าใจข้อจำกัดของการใช้เธรดใน Python
เธรดและ GIL เป็นบทเรียน Python Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Python Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Python Academy มีบทเรียนทั้งหมด 4 บทเรียน
Thread คืออะไร
เธรดคือกระแสการทำงานแยกต่างหากภายในกระบวนการเดียว โมดูลสำหรับการทำงานแบบเธรดของ Python ช่วยให้คุณเรียกใช้ฟังก์ชันพร้อมกันภายในโปรแกรมเดียวได้
import threading
def worker():
print('hello from thread')
t = threading.Thread(target=worker)
t.start()
t.join()
print('main done')การเริ่มต้นเธรดหลายรายการ
คุณสามารถเปิดใช้งานเธรดหลายรายการได้ แต่ละเธรดจะเรียกใช้ฟังก์ชันเป้าหมายของตนเอง join() ทำให้เธรดหลักรอจนกว่าเธรดหนึ่งจะทำงานเสร็จ
import threading
def task(name):
print('task', name)
threads = [threading.Thread(target=task, args=(i,)) for i in range(3)]
for t in threads:
t.start()
for t in threads:
t.join()
print('all finished')ล็อกตัวแปลภาษาส่วนกลาง
CPython มีล็อกตัวแปลภาษาส่วนกลาง (GIL) ซึ่งอนุญาตให้มีเธรดเดียวทำงานกับไบต์โค้ดของ Python ในแต่ละครั้ง ดังนั้นเธรดจึงไม่ช่วยเพิ่มความเร็วแบบขนานอย่างแท้จริงสำหรับงาน CPU ที่ใช้ Python ล้วน
import sys
print('Running on:', sys.implementation.name)
print('Only one thread runs Python bytecode at a time due to the GIL')กรณีที่เธรดยังมีประโยชน์
เธรดเหมาะอย่างยิ่งกับงานที่รอ I/O เช่น คำขอเครือข่าย การอ่านดิสก์ และการรอคอย ระหว่างที่เธรดหนึ่งรอ GIL จะถูกปล่อย และเธรดอื่นสามารถทำงานได้
import threading, time
def fake_io(name):
time.sleep(0.1)
print('done', name)
start = time.perf_counter()
ts = [threading.Thread(target=fake_io, args=(i,)) for i in range(3)]
for t in ts: t.start()
for t in ts: t.join()
print('elapsed about 0.1s, not 0.3s')งานที่ใช้ CPU ไม่เร็วขึ้น
สำหรับการคำนวณหนัก การเพิ่มเธรดจะไม่ทำให้เร็วขึ้น เพราะ GIL บังคับให้เธรดทำงานทีละตัว สำหรับงานที่ใช้ CPU ให้ใช้ multiprocessing แทน
import threading
def burn():
total = 0
for i in range(1000000):
total += i
return total
t = threading.Thread(target=burn)
t.start()
t.join()
print('CPU-bound: threads share one core under the GIL')เธรดดีมอน
เธรดดีมอนจะถูกหยุดโดยอัตโนมัติเมื่อโปรแกรมหลักจบการทำงาน ใช้เธรดประเภทนี้กับตัวช่วยเบื้องหลังที่คุณไม่จำเป็นต้องรอ
import threading, time
def background():
time.sleep(0.05)
print('background tick')
t = threading.Thread(target=background, daemon=True)
t.start()
t.join()
print('main exits')การตั้งชื่อและระบุเธรด
เธรดแต่ละรายการมีชื่อและรหัสของตนเอง threading.current_thread() จะคืนค่าออบเจ็กต์ของเธรดที่กำลังทำงาน ซึ่งมีประโยชน์สำหรับการบันทึกข้อมูล
import threading
def show():
t = threading.current_thread()
print('running in', t.name)
t = threading.Thread(target=show, name='Worker-1')
t.start()
t.join()
print('main thread:', threading.current_thread().name)การสร้างคลาสย่อยของ Thread
แทนที่จะส่งเป้าหมาย คุณสามารถสร้างคลาสย่อยของ Thread และเขียนทับ run() ได้ วิธีนี้มีประโยชน์เมื่อเธรดต้องมีสถานะของตัวเอง
import threading
class Greeter(threading.Thread):
def __init__(self, name):
super().__init__()
self.who = name
def run(self):
print('hi', self.who)
g = Greeter('Sam')
g.start()
g.join()การคืนผลลัพธ์จากเธรด
ค่าที่ฟังก์ชันเป้าหมายคืนมาจะถูกทิ้ง หากต้องการรวบรวมผลลัพธ์ ให้เขียนผลลัพธ์ลงในโครงสร้างข้อมูลที่ใช้ร่วมกัน (ภายหลังคุณจะได้รู้จักเครื่องมือที่ปลอดภัยกว่า เช่น คิว)
import threading
results = {}
def compute(key):
results[key] = key * key
ts = [threading.Thread(target=compute, args=(i,)) for i in range(4)]
for t in ts: t.start()
for t in ts: t.join()
print(results)การนับเธรดที่ทำงานอยู่
threading.active_count() จะบอกจำนวนเธรดที่ยังทำงานอยู่ในปัจจุบัน รวมถึงเธรดหลักด้วย
import threading, time
def wait():
time.sleep(0.05)
ts = [threading.Thread(target=wait) for _ in range(2)]
for t in ts: t.start()
print('active now:', threading.active_count())
for t in ts: t.join()
print('active after join:', threading.active_count())การเลือกใช้เธรดหรือกระบวนการ
หลักทั่วไปคือ ใช้เธรดสำหรับการทำงานพร้อมกันที่รอ I/O และใช้กระบวนการสำหรับการทำงานแบบขนานที่ใช้ CPU สาเหตุคือ GIL
tasks = {'download files': 'threads', 'crunch numbers': 'processes', 'call APIs': 'threads'}
for task, choice in tasks.items():
print(task, '->', choice)ตรวจสอบอย่างรวดเร็ว
ทดสอบความเข้าใจของคุณเกี่ยวกับเธรดและ GIL
ทบทวน
คุณได้เรียนรู้เกี่ยวกับเธรดและ GILแล้ว:
- เธรดทำงานพร้อมกันภายในกระบวนการเดียวผ่าน
threading.Thread - GIL ทำให้การทำงานของไบต์โค้ด Python ต้องดำเนินไปทีละรายการ
- เธรดเหมาะกับงานที่รอ I/O ส่วนกระบวนการเหมาะกับงานที่ใช้ CPU
- ใช้
start(),join(), เธรดดีมอน และการสร้างคลาสย่อย
ถัดไปคือการจัดการเธรดอย่างเป็นระเบียบด้วย ThreadPoolExecutor
คำถามที่พบบ่อย
บทเรียน “เธรดและ GIL” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “เธรดและ GIL” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Python Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Python Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “เธรดและ GIL”
ทำความเข้าใจข้อจำกัดของการใช้เธรดใน Python คุณปฏิบัติ Python Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Python Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Python Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน
บทเรียน “เธรดและ GIL” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Python Academy นี้ได้ไหม
ได้ บทเรียน Python Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ