0Pricing
Python Academy · บทเรียน

เธรดและ 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. เธรดและ GIL
  2. ThreadPoolExecutor
  3. พื้นฐาน multiprocessing
  4. การแบ่งปันข้อมูลอย่างปลอดภัย
← กลับไปที่ Python Academy