1. Ano ang thread sa Python?

Ang thread sa Python ay isang mekanismong nagbibigay-daan para magsagawa ng maraming gawain nang sabay-sabay sa loob ng isang programa. Sa paggamit ng mga thread, maaaring tumakbo nang magkakasabay ang ilang bahagi ng programa nang hindi naghihintay sa iba, kaya mas episyente ang pagproseso. Sa Python, maaari kang lumikha at mamahala ng mga thread gamit ang modyul na threading.

Pangunahing konsepto ng thread

Ang thread ay magaang yunit ng pagpapatupad na tumatakbo sa loob ng isang proseso. Sa isang proseso, maaaring tumakbo ang maraming thread; dahil gumagana silang magkakahiwalay, naisasakatuparan ang sabayang pagproseso ng programa. Lalo itong epektibo para sa mga I/O na operasyon (pagbasa at pagsulat ng file at komunikasyong pang-network) at sa pagpapabuti ng pagtugon ng user interface.

Mga halimbawa ng paggamit ng thread sa Python

Halimbawa, kapag gumagawa ng web scraping tool, mapapaikli ang kabuuang oras ng pagproseso sa pamamagitan ng sabayang pag-access sa maraming web page. Gayundin, sa mga aplikasyong nagpoproseso ng data nang real time, maaari mong i-update ang data sa background nang hindi pinatitigil ang pangunahing pagproseso.

2. Pag-unawa sa Global Interpreter Lock(GIL) sa Python

Sa mga thread ng Python, ang Global Interpreter Lock(GIL) ay isang napakahalagang konsepto. Ang GIL ay isang mekanismong naglilimita upang ang Python interpreter ay makapagpatakbo lamang ng isang thread sa isang pagkakataon。

Epekto ng GIL

Pinipigilan ng GIL ang sabayang pagtakbo ng mga thread at pinananatili ang pagkakapare-pareho ng pamamahala ng memorya sa loob ng iisang proseso. Gayunman, dahil sa limitasyong ito, sa mga gawaing CPU-bound(mga prosesong mabigat sa CPU) ay nalilimitahan ang mga pakinabang ng parallel na pagproseso gamit ang mga thread. Halimbawa, kahit magsagawa ng masalimuot na mga kalkulasyon sa maraming thread, dahil sa GIL ay iisa lamang ang naipapatakbo sa anumang oras, kaya hindi nakakamit ang inaasahang pagbuti ng pagganap。

Paano iwasan ang GIL

Upang maiwasan ang mga limitasyon ng GIL, epektibo ang pagparalelo ng mga proseso gamit ang multiprocessing na module. Sa multiprocessing, bawat proseso ay may sariling Python interpreter, kaya maaaring isagawa ang parallel na pagproseso nang hindi naaapektuhan ng GIL。

3. Pangunahing paggamit ng modulo ng Python na threading

Ang modulo na threading ay isang pamantayang library para sa paglikha at pamamahala ng mga thread sa Python. Dito, ipapaliwanag ang pangunahing paggamit.

Paglikha at pagpapatakbo ng thread

Upang makalikha ng thread, gamitin ang klaseng threading.Thread. Halimbawa, maaari kang lumikha at magpatakbo ng isang thread gaya ng nasa ibaba.
import threading
import time

def my_function():
    time.sleep(2)
    print("Thread executed")

# Paglikha ng thread
thread = threading.Thread(target=my_function)

# Pagsisimula ng thread
thread.start()

# Maghintay sa pagtatapos ng thread
thread.join()
print("Main thread completed")
Sa code na ito, nalilikha ang isang bagong thread at ang my_function ay isinasagawa nang asynchronous.

Sinkronisasyon ng thread

Upang hintayin ang pagtatapos ng thread, gamitin ang metodong join(). Ang metodong ito ay humihinto sa pagpapatakbo ng main thread hanggang sa matapos ang thread, kaya posible ang sinkronisasyon sa pagitan ng mga thread.

4. Gumawa ng thread sa pamamagitan ng pag-subclass sa Thread na klase

threading.Thread na klase ang i-subclass upang ma-customize mo ang mga thread nang mas flexible.
年収訴求

Paglikha ng subclass ng Thread

Gaya ng sumusunod, i-subclass ang klaseng Thread upang makagawa ng sarili mong klase ng thread, at i-override ang metodong run().
import threading
import time

class MyThread(threading.Thread):
    def run(self):
        time.sleep(2)
        print("Custom thread executed")

# Paglikha at pagpapatakbo ng custom na thread
thread = MyThread()
thread.start()
thread.join()
print("Main thread completed")

Mga pakinabang ng pag-subclass

Sa pamamagitan ng pag-subclass, maaari mong i-enkapsula ang ginagawa ng thread at makapagsulat ng code na madaling magamit muli. Bukod pa rito, posible ang pamamahala ng thread na may kakayahang umangkop, gaya ng pagbibigay ng magkakaibang data sa bawat thread.

5. Kaligtasan at pagsabay ng mga thread

Kapag maraming thread ang sabay na nag-a-access sa iisang resource, kailangan ang pagsabay upang mapanatili ang integridad ng data.

Race condition

Ang race condition ay isang sitwasyon kung saan sabay-sabay na binabago ng maraming thread ang iisang resource, na nagdudulot ng hindi inaasahang mga resulta. Halimbawa, kapag pinapataas ang isang counter variable mula sa maraming thread, kung walang angkop na pagsabay, maaaring hindi makuha ang tamang resulta.

Pagsabay gamit ang lock

Sa threading module, mayroong Lock object para sa pagsabay ng mga thread. Sa paggamit ng Lock, habang ginagamit ng isang thread ang isang resource, mapipigilan mong ma-access iyon ng iba pang mga 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)
Sa halimbawang ito, dahil pinapataas ang counter lamang sa loob ng with lock block, napapanatili ang integridad ng data.

6. Mga thread at mga I/O-bound vs CPU-bound na gawain

Ang mga thread ay partikular na epektibo para sa mga I/O-bound na gawain (mga operasyon sa file, komunikasyon sa network, atbp.).

Mga bentahe ng mga thread sa I/O-bound na mga gawain

Ang mga I/O-bound na gawain ay madalas na gumugugol ng maraming oras sa paghihintay habang isinasagawa ang pagproseso, kaya sa paggamit ng mga thread upang iproseso nang magkasabay ang iba pang mga gawain, maaaring mapahusay ang pangkalahatang kahusayan ng programa. Halimbawa, sa sabay na pagpapatakbo ng thread para sa pagbasa at pagsulat ng mga file at ng thread para sa komunikasyon sa network, maaaring mabawasan ang oras ng paghihintay.

Mga CPU-bound na gawain atmultiprocessing

Para sa mga CPU-bound na gawain (mga kalkulasyong numerikal, pagproseso ng datos, atbp.), inirerekomenda ang paggamit ng modyul na multiprocessing sa halip na threading. Dahil ang multiprocessing ay hindi naaapektuhan ng GIL, maaaring mapakinabangan ang mga multi-core na processor upang mapahusay ang pagganap.

7. Pamamahala ng mga Thread

Ipapaliwanag ang mga teknik para epektibong pamahalaan ang mga thread sa Python.

Pagpapangalan at Pagkilala sa mga Thread

Sa pamamagitan ng pagpapangalan sa mga thread, mas madali silang makilala kapag nagde-debug o naglalabas ng log. Maaari mong itakda ang pangalan ng thread gamit ang argumentong name ng threading.Thread.
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()

Pagsuri ng estado ng Thread

Upang malaman kung kasalukuyang tumatakbo ang isang thread, gamitin ang metodong is_alive(). Ibinabalik ng metodong ito ang True kung tumatakbo pa ang thread, at False kung natapos na. Sa wastong pamamahala ng estado ng thread, maiiwasan ang hindi inaasahang pag-uugali ng programa.
import threading
import time

def task():
    time.sleep(1)
    print("Task completed")

thread = threading.Thread(target=task)
thread.start()

# Suriin kung kasalukuyang tumatakbo ang thread
if thread.is_alive():
    print("Thread is still running")
else:
    print("Thread has finished")

Pagpapatigil ng Thread

Sa Python, walang direktang paraan sa moduleng threading para ihinto ang isang thread. Ito ay dahil ang sapilitang pagpapatigil ng thread ay maaaring magdulot ng hindi pagkakatugma ng datos o pagkabigong mag-release ng mga resource. Upang ligtas na ihinto ang thread, karaniwang nagtatakda ng isang flag sa loop na tumatakbo upang kontrolin ang pagwawakas.
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()

time.sleep(5)
stop_thread = True
thread.join()
print("Thread has been stopped")

8. Paghahambing ng mga thread at multiprocessing

Mahalagang maunawaan ang pagkakaiba ng mga thread at mga proseso, at malaman kung kailan angkop gamitin ang bawat isa.

Mga bentahe at kahinaan ng mga thread

Magaan ang mga thread at maaaring magbahagi ng memorya sa loob ng iisang proseso, kaya mababa ang overhead at angkop para sa mga I/O-bound na gawain. Gayunman, gaya ng nabanggit, maaaring malimitahan ang pagganap ng mga CPU-bound na gawain dahil sa GIL ng Python.

Mga bentahe ng multiprocessing na modyul

Sa multiprocessing na modyul, dahil may kanya-kanyang independiyenteng Python interpreter ang bawat proseso, magagamit nang lubos ang mga CPU core nang hindi naaapektuhan ng GIL. Malaking bentahe ito para sa mga CPU-bound na gawain. Gayunman, upang magbahagi ng data sa pagitan ng mga proseso, kailangang gumamit ng mga pipe o queue, kaya mas malaki ang overhead kumpara sa mga thread.

Mga gabay sa pagpili kung alin ang gagamitin

  • Kapag gagamit ng mga thread: mga I/O-bound na gawain, pagpapahusay ng responsiveness ng mga GUI application, at iba pa—mga sitwasyong hindi gaanong naaapektuhan ng GIL.
  • Kapag gagamit ng multiprocessing: mga CPU-bound na gawain, kapag kailangan ng advanced na parallel processing, at iba pa—mga kaso kung saan kailangang iwasan ang mga limitasyon ng GIL.

9. Mga pinakamahusay na gawi para sa module na threading ng Python

Sa multithreaded na programming, sa pagsunod sa ilang pinakamahusay na gawi, maaari mong matiyak ang matatag na pagtakbo at kadalian ng pag-debug.

Ligtas na pagtatapos ng mga thread

Iwasan ang sapilitang pagpapahinto ng mga thread; gumamit ng mga flag at condition variable upang ligtas silang maihinto. Kapag gumagamit ang thread ng mga resource, tiyaking magpatupad ng code upang i-release ang mga ito.

Iwasan ang deadlock

Kapag gumagamit ng lock para sa pag-synchronize ng mga thread, bigyang-pansin ang mga sumusunod upang maiwasan ang deadlock.
  • Magtakda ng pagkakasunod-sunod sa pagkuha ng lock at panatilihin ang pagiging pare-pareho.
  • Kunin ang lock sa pinakamaliit na kinakailangang saklaw lamang.
  • Kung maaari, gumamit ng with statement upang awtomatikong i-release ang lock.

Pag-debug at pag-log

Ang mga programang gumagamit ng mga thread ay maaaring maging mahirap i-debug. Dahil dito, gamitin ang pag-log upang masubaybayan ang kilos ng mga thread. Sa paggamit ng logging module, ang pagtatala ng log kada thread ay nagpapadali sa pagtukoy ng mga problema.
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. Buod

Ang modyul na threading ng Python ay isang makapangyarihang kasangkapan para maisakatuparan ang sabayang pagproseso ng mga programa. Sa artikulong ito, tinalakay nang malawakan ang mga pangunahing paraan ng paggamit ng mga thread, ang epekto ng GIL, kung kailan gagamit ng mga thread kumpara sa multiprocessing, at ang mga pinakamahusay na gawi kapag gumagamit ng mga thread. Ang mga thread ay angkop para sa pagpapahusay ng kahusayan ng mga I/O-bound na gawain, ngunit mahalagang maunawaan ang pag-iral ng GIL at piliin nang tama kung kailan gagamit ng mga thread at kung kailan hindi. Sa pamamagitan ng pagbibigay-pansin sa pamamahala at kaligtasan ng mga thread, at sa pagpili ng pinakamainam na pamamaraan sa pagpoproseso, maaari mong mapahusay ang pagganap at pagiging maaasahan ng mga programang Python. Sa hinaharap, kung nais mong sumubok ng mas advanced na pagpoproseso gamit ang mga thread at sabayang pagpoprograma, sumangguni sa opisyal na dokumentasyon at mga aklat na pang-espesyalista upang higit pang palalimin ang iyong pag-unawa。