Multithreading & Multiprocessing Python
Dalam komputasi modern, kita sering mendengar istilah parallelism dan concurrency. Di Python, ada dua cara utama untuk melakukan banyak hal "sekaligus": Multithreading dan Multiprocessing.
Pemilihan antara keduanya sangat bergantung pada jenis tugas yang Anda kerjakan: apakah I/O Bound atau CPU Bound.
1. I/O Bound vs CPU Bound
- I/O Bound: Program menghabiskan sebagian besar waktunya menunggu input/output (misalnya: request jaringan, membaca file disk, query database). CPU sering menganggur.
- CPU Bound: Program menghabiskan waktunya untuk melakukan perhitungan matematika atau pemrosesan data yang berat. CPU bekerja 100%.
2. Multithreading (Untuk I/O Bound)
Threading menggunakan thread di dalam satu proses yang sama. Thread berbagi memori yang sama.
Namun, Python (CPython) memiliki GIL (Global Interpreter Lock), yang mencegah dua thread Python mengeksekusi bytecode secara bersamaan di satu core CPU. Jadi, Multithreading di Python tidak membuat kode CPU-bound lebih cepat (malah bisa lebih lambat karena overhead).
Tapi, Multithreading sangat cepat untuk I/O Bound karena saat satu thread menunggu (misal nunggu respons web), thread lain bisa berjalan.
import threading
import time
def unduh_halaman(url):
print(f"Mulai mengunduh {url}...")
time.sleep(2) # Simulasi delay jaringan
print(f"Selesai mengunduh {url}")
start = time.time()
threads = []
urls = ["web1", "web2", "web3"]
for url in urls:
t = threading.Thread(target=unduh_halaman, args=(url,))
threads.append(t)
t.start()
# Tunggu semua thread selesai
for t in threads:
t.join()
end = time.time()
print(f"Total waktu: {end - start:.2f} detik")
# Output sekitar 2 detik, bukan 6 detik!
3. Multiprocessing (Untuk CPU Bound)
Multiprocessing membuat proses Python baru yang terpisah. Setiap proses memiliki Python interpreter dan ruang memorinya sendiri. Ini membypasses GIL, sehingga bisa menggunakan multi-core CPU secara maksimal.
Gunakan ini untuk tugas komputasi berat.
import multiprocessing
import time
def hitung_kuadrat_berat(angka):
print(f"Proses {angka} mulai...")
hasil = sum(i * i for i in range(10**7)) # Perhitungan berat
print(f"Proses {angka} selesai.")
return hasil
if __name__ == "__main__":
start = time.time()
# Membuat 2 proses berjalan paralel di core CPU berbeda
p1 = multiprocessing.Process(target=hitung_kuadrat_berat, args=(1,))
p2 = multiprocessing.Process(target=hitung_kuadrat_berat, args=(2,))
p1.start()
p2.start()
p1.join()
p2.join()
end = time.time()
print(f"Total waktu: {end - start:.2f} detik")
Catatan: Anda harus melindungi kode utama dengan if __name__ == "__main__": saat menggunakan multiprocessing di Windows.
4. Concurrent Futures (Cara Modern)
Python menyediakan modul concurrent.futures yang memberikan interface lebih tinggi dan mudah untuk Threading dan Multiprocessing.
from concurrent.futures import ThreadPoolExecutor
import time
def tugas(n):
time.sleep(1)
return f"Tugas {n} selesai"
start = time.time()
with ThreadPoolExecutor(max_workers=3) as executor:
results = executor.map(tugas, [1, 2, 3])
for result in results:
print(result)
print(f"Waktu: {time.time() - start:.2f} detik")
Ganti ThreadPoolExecutor dengan ProcessPoolExecutor jika ingin beralih ke multiprocessing.
Kesimpulan
| Fitur | Multithreading | Multiprocessing |
|---|---|---|
| Memori | Berbagi memori (Shared) | Memori terpisah (Isolated) |
| Overhead | Rendah | Tinggi (butuh waktu start) |
| Cocok untuk | I/O Bound (Network, File) | CPU Bound (Math, Data Processing) |
| GIL | Terkena dampak GIL | Bebas dari GIL |